I'm getting a "RateLimitError: An internal error occurred at async handler" error

I’m invoking my Fleet agent from my React code through the Langsmith SDK and I’m getting a “RateLimitError: An internal error occurred at async handler” error.

The error is highlighting this line from my code specifically

const result = await client.runs.wait

It’s always been working before but now not anymore even if I didn’t change anything in Langsmith Fleet or my code or my LLM.

Does anybody here have an idea how to solve this?

hi @AmineAouragh

That line is probably just where the deployed agent failure is being surfaced, not the actual cause.

client.runs.wait(...) in the LangGraph SDK can fail in two different ways that are easy to confuse:

  1. The HTTP request to Fleet/LangSmith itself returns an HTTP error, for example a real 429 Too Many Requests.
  2. The request succeeds, the agent run executes on the server, but the run finishes with __error__; by default runs.wait re-throws that run-level error.

The second case is very likely here. In the SDK source, runs.wait checks the returned run values and, if raiseError is enabled, throws:

`${run.__error__?.error}: ${run.__error__?.message}`

So if the run object contains:

{
  "__error__": {
    "error": "RateLimitError",
    "message": "An internal error occurred at async handler"
  }
}

then your caller sees:

RateLimitError: An internal error occurred at async handler

That usually means the model/tool call inside the Fleet agent hit a provider-side rate limit or transient provider/gateway error. It can start happening even if you did not change your code: provider quota, credits, rate-limit tier, API key state, traffic volume, or provider availability can change independently.

The first thing I would do is confirm which case you are in:

try {
  const result = await client.runs.wait(null, agentId, {
    input: {
      messages: [{ role: "user", content: userMessage }],
    },
  });
  return result;
} catch (e: any) {
  console.error("Fleet run failed", {
    name: e?.name,
    message: e?.message,
    status: e?.status ?? e?.statusCode,
    headers: e?.headers,
    cause: e?.cause,
  });
  throw e;
}

If you see an actual status / statusCode of 429, then you are hitting a platform/API rate-limit path and should back off, reduce request concurrency, and check LangSmith/Fleet rate limits.

If there is no HTTP 429 and the message is just RateLimitError: ..., open the failed run trace in LangSmith and inspect the failing model/child span. That trace should tell you which provider/model failed and whether it was quota, credits, key state, provider overload, or another provider-side issue.

Fixes to try:

  1. Verify the model provider key used by the Fleet deployment is still valid, funded, and attached to the expected provider org/project.
  2. Check provider quota/rate-limit dashboards and status page.
  3. If you control/export the agent code, add resilience there and redeploy: model retries (max_retries, .with_retry() / .withRetry()), a rate limiter such as InMemoryRateLimiter, or model/tool call caps such as modelCallLimitMiddleware / toolCallLimitMiddleware.
  4. If you want to inspect run-level failures without runs.wait throwing, call it with raiseError: false and check result.__error__ yourself:
const result = await client.runs.wait(null, agentId, {
  input: {
    messages: [{ role: "user", content: userMessage }],
  },
  raiseError: false,
});

if ((result as any)?.__error__) {
  console.log((result as any).__error__);
}

Important caveat: raiseError: false only helps for run-level __error__ responses. It will not prevent an actual HTTP 429 from the API/platform from throwing.

Two React-specific things worth checking:

  • If you are calling Fleet directly from browser React with a LangSmith PAT/API key, move that call behind a backend/API route. Otherwise the token is exposed to users, and you cannot centrally throttle/retry.
  • Check for duplicate calls from React Strict Mode, repeated effects, re-renders, multiple tabs, or frontend retries. Those can silently double or multiply traffic and trigger rate limits.

Docs:

Hi I get a 500 Internal Server Error which makes me wonder: is the error on my side really or is it something going on on the Langsmith server which I can do nothing about.

Hi Amine, can you please share some more details on how you are invoking the agent (small code snippet would be helpful), also a trace and/or request ID.

Hi Jeibar,

Yes sure, here is the code (btw it used to work perfectly before but not anymore and idk why)

import { Client } from "@langchain/langgraph-sdk"

export default async function handler(req, res){

    if (req.method != 'POST'){
        return res.status(405).json({ error: 'Method not allowed' })
    }

    const { prompt } = req.body
    
    const agentId = process.env.PAOLO_AGENT_ID
    const apiKey = process.env.LANGGRAPH_API_KEY
    const apiUrl = process.env.LANGSMITH_API_URL

    if (!agentId || !apiKey || !apiUrl){
        console.log("missing agent id or key or api url")
    }

    const client = new Client({
        apiUrl, 
        apiKey, 
        defaultHeaders: {
            "X-Auth-Scheme": "langsmith-api-key"
        }
    })

    if (!client){
        console.log("client configuration not set correctly")
    }

    try {
        const result = await client.runs.wait(
            null,
            agentId,
            {
                input: {
                    messages: [{ role: "user", content: prompt}]
                }
            }
        ) 
        if (result){
            return res.status(201).json({ result: result })
        } else {
            return res.status(500).json({ error: "error occurred while trying to invoke your agent" })
        }
    } catch (error) {
        console.log("error occurred while trying to invoke your agent: ", error)
    }
}

Thanks @AmineAouragh, this helps. Two separate things are going on IMO.

  1. The 500 is almost certainly your own API route, not LangSmith. Your catch logs the error but never calls res, so Next.js resolves the request without a response and the browser gets a 500/hanging request. Fix the catch first so you see the real error end-to-end:
} catch (error) {
  const status = error?.status ?? error?.statusCode; // present only for HTTP errors

  console.error("Fleet call failed", {
    name: error?.name,
    message: error?.message,
    status,
    headers: error?.headers,
  });

  return res.status(status ?? 502).json({
    error: error?.message ?? "Fleet call failed",
    status: status ?? null,
  });
}

Also make the env check return instead of only logging, and verify apiUrl is the Agent Server URL from “View code snippets” (https://<AGENT-BUILDER-URL>.us.langgraph.app), not the general LangSmith API URL.

  1. RateLimitError: An internal error occurred at async handler comes from inside the run, not from your HTTP call. client.runs.wait rebuilds that string from the run’s __error__ field, so the request reached the server and the run itself failed - a real transport error would throw as HTTP 429: … / HTTP 500: … instead (and only those carry error.status; the run-level one is a plain Error with no status, which is why the field above will be undefined for your case). The “async handler” wording points at the Fleet/Agent-Server side.

To confirm and capture what staff need:

const result = await client.runs.wait(null, agentId, {
  input: { messages: [{ role: "user", content: prompt }] },
  raiseError: false,                                   // inspect instead of throwing
  onRunCreated: (m) => console.log("Run created:", m), // grab the run ID
});

if (result?.__error__) {
  console.error("Run-level error:", result.__error__);
}
  • If you get result.__error__ (or the caught error has no status) → the deployment call succeeded but the run failed server-side. Open that run’s trace in LangSmith and inspect the failing model/tool span.
  • If the error does carry status: 429/500 → the HTTP call to the deployment is failing; recheck apiUrl, auth, and agent ID.

Quick isolation test: await client.assistants.get(agentId) - if that works but runs.wait fails, it’s inside the run/provider, not your config.

Since this looks server-side, please share the run ID + trace URL + rough timestamp from the failed run for @jeibar to dig in.

Hi Pawel, here are my fleet run details as requested.

In the error details it says “Rate limit reached for gpt-5.4-mini in organization org-WOxxxxxxxxxxxx on tokens per min (TPM): Limit 200000, Used 106609, Requested 117713”

gpt-5.4-mini is the model I’m using. The token usage issue happened to me before but I tried again later and it worked. But now it’s not working. I don’t know how to keep the token usage under control. Like setting some sort of maximum threshold for example.

hi @AmineAouragh

Thanks - that trace settles it: you’re hitting the model provider’s per-minute token limit (TPM), not a Fleet/SDK bug.

The math: already used 106,609 + this run requested 117,713 = 224,322, which is above the 200,000 TPM limit → rejected.

“requested” can include the full model request: system prompt, conversation history, tool/retrieved context, and reserved max output tokens - not just the user prompt.

Fixes, in order:

  1. check the trace to see if the 117k is mostly input or reserved output
  2. cap max output tokens (maxTokens / “Max Output Tokens” in Agent Builder) - this directly lowers the “requested” count
  3. add summarization so long histories get compressed instead of resent every turn:
    summarizationMiddleware({ model, trigger: { tokens: 20000 }, keep: { messages: 10 } }), and trim large tool/retrieved context
  4. bump the model’s maxRetries - LangChain retries 429s with exponential backoff and jitter, so increasing maxRetries can help brief TPM spikes recover instead of surfacing to the user
  5. If you genuinely need this throughput, request a higher TPM tier from the provider

Note: a request-rate limiter only helps with concurrency - it won’t fix a single oversized request. The real levers are #2 and #3 (shrink tokens-per-request); #4 absorbs the occasional spike.

Docs: max tokens · summarization · retries