How to Fix 'rate limit exceeded during development' in LangGraph (TypeScript)
What the error means
rate limit exceeded during development usually means your LangGraph app is calling an upstream model provider too aggressively while you’re iterating locally. In practice, this shows up when you trigger multiple graph runs at once, stream repeatedly in a loop, or rebuild the same graph on every request and accidentally multiply calls.
The actual exception often comes from the provider SDK under LangGraph, for example:
OpenAIError: 429 Rate limit exceeded
AnthropicRateLimitError: Request rate limit exceeded
The Most Common Cause
The #1 cause is re-invoking the graph inside a retry loop or event handler without any throttling. In TypeScript, this often happens when you call graph.invoke() from code that already reruns on every keystroke, request, or UI render.
Broken vs fixed pattern
| Broken pattern | Fixed pattern |
|---|---|
| Calls the graph repeatedly with no backoff | Deduplicates requests and adds retry/backoff |
| Recreates the model/graph per request | Reuses a single compiled graph instance |
| Lets parallel calls pile up | Limits concurrency to 1 for local dev |
// BROKEN
import { StateGraph } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({ model: "gpt-4o-mini" });
async function runQuery(input: string) {
const graph = new StateGraph({
channels: {
messages: { value: (x: any[]) => x },
},
})
.addNode("llm", async (state: any) => {
const res = await model.invoke(state.messages);
return { messages: [...state.messages, res] };
})
.setEntryPoint("llm")
.compile();
return graph.invoke({ messages: [input] });
}
// Called multiple times by UI events / retries
setInterval(() => runQuery("Summarize this"), 200);
// FIXED
import { StateGraph } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import pLimit from "p-limit";
const model = new ChatOpenAI({ model: "gpt-4o-mini" });
const limit = pLimit(1);
const graph = new StateGraph({
channels: {
messages: { value: (x: any[]) => x },
},
})
.addNode("llm", async (state: any) => {
const res = await model.invoke(state.messages);
return { messages: [...state.messages, res] };
})
.setEntryPoint("llm")
.compile();
async function runQuery(input: string) {
return limit(() => graph.invoke({ messages: [input] }));
}
If you’re using a UI framework, this also includes accidental double execution from React Strict Mode or repeated server actions. The fix is the same: one compiled graph, one controlled execution path.
Other Possible Causes
1. Streaming without consuming events correctly
If you start multiple streams before the previous one finishes, you’ll burn through quota fast.
// BAD
for await (const chunk of graph.stream({ messages })) {
console.log(chunk);
}
// Somewhere else in parallel:
graph.stream({ messages });
Use a single stream per user action:
// GOOD
const stream = await graph.stream({ messages });
for await (const chunk of stream) {
console.log(chunk);
}
2. Recursive agent loops with no stop condition
A node that keeps routing back to itself can create an infinite call chain.
// BAD
builder.addConditionalEdges("agent", () => "agent");
Add a hard stop:
// GOOD
builder.addConditionalEdges("agent", (state) =>
state.steps.length >= 5 ? END : "agent"
);
If you’re using MessagesAnnotation or custom state, track step count explicitly.
3. Too many parallel tool calls
LangGraph agents can fan out into multiple tool invocations. If each tool calls an LLM, your local test can hit provider limits quickly.
// BAD: parallel fan-out with no cap
await Promise.all(tools.map((tool) => tool.invoke(input)));
Cap concurrency:
import pLimit from "p-limit";
const limit = pLimit(2);
await Promise.all(tools.map((tool) => limit(() => tool.invoke(input))));
4. Provider config is too aggressive for dev
Sometimes the issue is not LangGraph at all. Your provider settings may be too hot for local iteration.
const model = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0,
maxRetries: 0,
});
A better dev setup:
const model = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0,
maxRetries: 2,
});
Also check org-level quotas and per-minute limits in OpenAI, Anthropic, or Azure OpenAI.
How to Debug It
- •
Log every graph entry point
- •Add logs before
invoke(),stream(), and inside each node. - •If you see duplicate entry logs for one user action, you found the source.
- •Add logs before
- •
Inspect concurrency
- •Search for
Promise.all, intervals, event listeners, and UI re-renders. - •In dev tools or server logs, count how many requests start before one finishes.
- •Search for
- •
Check whether the graph is being rebuilt
- •If
compile()runs inside a request handler or component render, move it to module scope. - •A compiled
StateGraphshould usually be created once and reused.
- •If
- •
Look at the provider error details
- •OpenAI usually returns
429with rate-limit headers. - •Anthropic often throws
AnthropicRateLimitError. - •If the stack trace points to the SDK rather than LangGraph internals, it’s almost always traffic volume or recursion.
- •OpenAI usually returns
Prevention
- •Compile graphs once and reuse them across requests.
- •Add concurrency limits around user-triggered invocations during development.
- •Put a step cap on agent loops and recursive routing.
- •Keep retry logic explicit; don’t let UI code accidentally re-fire requests.
- •Monitor provider usage early with logs around each
invoke()andstream()call.
If you want a fast sanity check, look for this pattern first:
- •
compile()inside a function - •
invoke()called from a loop or render path - •missing stop condition in conditional edges
That combination causes most "rate limit exceeded during development" errors in LangGraph TypeScript projects.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit