recursionLimit config ignored — still getting limit of 25 #335

Open
opened 2026-02-15 18:15:59 -05:00 by yindo · 1 comment
Owner

Originally created by @hassaans on GitHub (Aug 12, 2025).

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangGraph.js documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangGraph.js rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package).

Example Code

import { StateGraph, END } from "@langchain/langgraph";

// Simple state type
type State = {
  count: number;
};

const builder = new StateGraph<State>({
  channels: {
    count: {
      default: () => 0,
    },
  },
});

// Node that increments count and loops until 100
builder.addNode("loopNode", (state) => {
  if (state.count >= 100) {
    return END;
  }
  return { count: state.count + 1 };
});

// Loop back to itself
builder.addEdge("loopNode", "loopNode");

// Set entry point
builder.setEntryPoint("loopNode");

export const graph = builder
  .compile()
  .withConfig({ runName: "assistant", recursionLimit: 256 });

(async () => {
  const finalState = await graph.invoke({ count: 0 });
  console.log("Final state:", finalState);
})();

Error Message and Stack Trace (if applicable)

Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key.

Troubleshooting URL: https://js.langchain.com/docs/troubleshooting/errors/GRAPH_RECURSION_LIMIT/


GraphRecursionError: Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key.

Troubleshooting URL: https://js.langchain.com/docs/troubleshooting/errors/GRAPH_RECURSION_LIMIT/

    at CompiledStateGraph._runLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1915:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async createAndRunLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1788:8)

Description

I've set the recursionLimit to 256 using the following code:

export const graph = builder
  .compile()
  .withConfig({ runName: "assistant", recursionLimit: 256 });

However, I’m still hitting this error:

Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key.

Troubleshooting URL: https://js.langchain.com/docs/troubleshooting/errors/GRAPH_RECURSION_LIMIT/

GraphRecursionError: Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key.

    at CompiledStateGraph._runLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1915:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async createAndRunLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1788:8)

Expected Behavior:
The recursionLimit value passed in .withConfig() should override the default of 25.

Actual Behavior:
Even with .withConfig({ recursionLimit: 256 }), the runtime still enforces a limit of 25.

System Info

Node version: v20.18.2
Operating system: darwin arm64
Package manager: npm
Package manager version: undefined

error: ▪ TypeError: str.matchAll(...).map is not a function
at gatherMatch (file:///Users/hassaansaleemmacbook2025/.npm/_npx/8b41b8d3955bcda6/node_modules/@langchain/langgraph-cli/dist/cli/sysinfo.mjs:34:48)
at Command. (file:///Users/hassaansaleemmacbook2025/.npm/_npx/8b41b8d3955bcda6/node_modules/@langchain/langgraph-cli/dist/cli/sysinfo.mjs:36:22)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Originally created by @hassaans on GitHub (Aug 12, 2025). ### Checked other resources - [x] I added a very descriptive title to this issue. - [x] I searched the LangGraph.js documentation with the integrated search. - [x] I used the GitHub search to find a similar question and didn't find it. - [x] I am sure that this is a bug in LangGraph.js rather than my code. - [x] The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package). ### Example Code ```typescript import { StateGraph, END } from "@langchain/langgraph"; // Simple state type type State = { count: number; }; const builder = new StateGraph<State>({ channels: { count: { default: () => 0, }, }, }); // Node that increments count and loops until 100 builder.addNode("loopNode", (state) => { if (state.count >= 100) { return END; } return { count: state.count + 1 }; }); // Loop back to itself builder.addEdge("loopNode", "loopNode"); // Set entry point builder.setEntryPoint("loopNode"); export const graph = builder .compile() .withConfig({ runName: "assistant", recursionLimit: 256 }); (async () => { const finalState = await graph.invoke({ count: 0 }); console.log("Final state:", finalState); })(); ``` ### Error Message and Stack Trace (if applicable) ``` Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key. Troubleshooting URL: https://js.langchain.com/docs/troubleshooting/errors/GRAPH_RECURSION_LIMIT/ GraphRecursionError: Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key. Troubleshooting URL: https://js.langchain.com/docs/troubleshooting/errors/GRAPH_RECURSION_LIMIT/ at CompiledStateGraph._runLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1915:17) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async createAndRunLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1788:8) ``` ### Description I've set the recursionLimit to 256 using the following code: ``` export const graph = builder .compile() .withConfig({ runName: "assistant", recursionLimit: 256 }); ``` However, I’m still hitting this error: ``` Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key. Troubleshooting URL: https://js.langchain.com/docs/troubleshooting/errors/GRAPH_RECURSION_LIMIT/ GraphRecursionError: Recursion limit of 25 reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key. at CompiledStateGraph._runLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1915:17) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async createAndRunLoop (/deps/langgraph-assistant-chat/node_modules/@langchain/langgraph/src/pregel/index.ts:1788:8) ``` Expected Behavior: The recursionLimit value passed in `.withConfig()` should override the default of 25. Actual Behavior: Even with `.withConfig({ recursionLimit: 256 })`, the runtime still enforces a limit of 25. ### System Info Node version: v20.18.2 Operating system: darwin arm64 Package manager: npm Package manager version: undefined -------------------- error: ▪ TypeError: str.matchAll(...).map is not a function at gatherMatch (file:///Users/hassaansaleemmacbook2025/.npm/_npx/8b41b8d3955bcda6/node_modules/@langchain/langgraph-cli/dist/cli/sysinfo.mjs:34:48) at Command.<anonymous> (file:///Users/hassaansaleemmacbook2025/.npm/_npx/8b41b8d3955bcda6/node_modules/@langchain/langgraph-cli/dist/cli/sysinfo.mjs:36:22) at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Author
Owner

@SunHuawei commented on GitHub (Sep 27, 2025):

Hey @hassaans
TLDR: The bug report doesn’t repro on @langchain/langgraph@0.4.9 + Node@20.18.2. The bump we hit was just from returning END straight out of the node. Once the node returns state and we let conditional edges handle the stop, everything lines up.
Here's the detail:
Running the code exactly like the issue showed gave me this:

> npx tsx issue-1524.ts
.../langgraphjs/issues-1651/node_modules/@langchain/langgraph/dist/graph/state.js:568
                throw new InvalidUpdateError(`Expected node "${nodeKey.toString()}" to return an object or an array containing at least one Command object, received ${typeofInput}`, {
                      ^

InvalidUpdateError: Expected node "loopNode" to return an object or an array containing at least one Command object, received string

Troubleshooting URL: https://langchain-ai.github.io/langgraphjs/troubleshooting/errors/INVALID_GRAPH_NODE_RETURN_VALUE/

    at RunnableCallable._getUpdates (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/graph/state.ts:996:15)
    at <anonymous> (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/utils.ts:87:26)
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at AsyncLocalStorageProvider.runWithConfig (file://~/langgraphjs/issues-1651/node_modules/@langchain/core/dist/singletons/async_local_storage/index.js:56:24)
    at RunnableCallable.invoke (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/utils.ts:85:62)
    at Function.doWrite (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/pregel/write.ts:123:45)
    at ChannelWrite._write (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/pregel/write.ts:90:24)
    at ChannelWrite.func (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/pregel/write.ts:65:21)
    at <anonymous> (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/utils.ts:57:39)
    at AsyncLocalStorage.run (node:async_hooks:346:14) {
  lc_error_code: 'INVALID_GRAPH_NODE_RETURN_VALUE',
  pregelTaskId: 'd3b0479f-f655-5859-ba5b-f0b124e9e9fa'
}

That pops up because the node hands back END directly. Since END is just a plain string, LangGraph complains—nodes need to hand back a state object (or at least one Command).

Swapping the self-loop for a conditional edge does the trick:

builder.addNode("loopNode", (state) => ({
  count: state.count + 1,
}));

builder.addConditionalEdges("loopNode", (state) =>
  state.count >= 100 ? END : "loopNode",
);

This way, the node always returns a valid state, and the conditional edge decides whether we stop or keep looping.

After the tweak, running compiled.withConfig({ recursionLimit: 256 }) works fine and ends with { count: 100 }, so the new limit is picked up.

PS, I went through the current LangGraph.js docs and couldn’t find any example that returns END from inside a node in JavaScript. My guess is the snippet in the issue was lifted from older material or from the Python docs, where returning END is allowed as long as the node registers ends=["next", END]. In JS, though, you probably can return only a normal state object and use a conditional edge to route to END.

@SunHuawei commented on GitHub (Sep 27, 2025): Hey @hassaans TLDR: The bug report doesn’t repro on `@langchain/langgraph@0.4.9` + Node@20.18.2. The bump we hit was just from returning `END` straight out of the node. Once the node returns state and we let conditional edges handle the stop, everything lines up. Here's the detail: Running the code exactly like the issue showed gave me this: ``` > npx tsx issue-1524.ts .../langgraphjs/issues-1651/node_modules/@langchain/langgraph/dist/graph/state.js:568 throw new InvalidUpdateError(`Expected node "${nodeKey.toString()}" to return an object or an array containing at least one Command object, received ${typeofInput}`, { ^ InvalidUpdateError: Expected node "loopNode" to return an object or an array containing at least one Command object, received string Troubleshooting URL: https://langchain-ai.github.io/langgraphjs/troubleshooting/errors/INVALID_GRAPH_NODE_RETURN_VALUE/ at RunnableCallable._getUpdates (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/graph/state.ts:996:15) at <anonymous> (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/utils.ts:87:26) at AsyncLocalStorage.run (node:async_hooks:346:14) at AsyncLocalStorageProvider.runWithConfig (file://~/langgraphjs/issues-1651/node_modules/@langchain/core/dist/singletons/async_local_storage/index.js:56:24) at RunnableCallable.invoke (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/utils.ts:85:62) at Function.doWrite (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/pregel/write.ts:123:45) at ChannelWrite._write (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/pregel/write.ts:90:24) at ChannelWrite.func (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/pregel/write.ts:65:21) at <anonymous> (~/langgraphjs/issues-1651/node_modules/@langchain/langgraph/src/utils.ts:57:39) at AsyncLocalStorage.run (node:async_hooks:346:14) { lc_error_code: 'INVALID_GRAPH_NODE_RETURN_VALUE', pregelTaskId: 'd3b0479f-f655-5859-ba5b-f0b124e9e9fa' } ``` That pops up because the node hands back `END` directly. Since `END` is just a plain string, LangGraph complains—nodes need to hand back a state object (or at least one `Command`). Swapping the self-loop for a conditional edge does the trick: ```ts builder.addNode("loopNode", (state) => ({ count: state.count + 1, })); builder.addConditionalEdges("loopNode", (state) => state.count >= 100 ? END : "loopNode", ); ``` This way, the node always returns a valid state, and the conditional edge decides whether we stop or keep looping. After the tweak, running `compiled.withConfig({ recursionLimit: 256 })` works fine and ends with `{ count: 100 }`, so the new limit is picked up. PS, I went through the current LangGraph.js docs and couldn’t find any example that returns `END` from inside a node in JavaScript. My guess is the snippet in the issue was lifted from older material or from the Python docs, where returning `END` is allowed as long as the node registers `ends=["next", END]`. In JS, though, you probably can return only a normal state object and use a conditional edge to route to `END`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#335