langchain-ai/langchain · critical · RecursionError
Recursion limit reached when invoking {self} with input {inp
Error message
Recursion limit reached when invoking {self} with input {input_}. What it means
In `RunnableLambda._invoke`, if the wrapped function returns a `Runnable`, the lambda transparently invokes it, decrementing `config['recursion_limit']` each level. When the limit reaches 0, a `RecursionError` is raised to stop unbounded (or accidentally infinite) runnables-returning-runnables chains. This is the same mechanism that halts runaway LangGraph-style loops built purely from runnables.
Source
Thrown at libs/core/langchain_core/runnables/base.py:5178
if output is None:
output = chunk
else:
try:
output = output + chunk # type: ignore[operator]
except TypeError:
output = chunk
else:
output = call_func_with_variable_args(
self.func, input_, config, run_manager, **kwargs
)
# If the output is a Runnable, invoke it
if isinstance(output, Runnable):
recursion_limit = config["recursion_limit"]
if recursion_limit <= 0:
msg = (
f"Recursion limit reached when invoking {self} with input {input_}."
)
raise RecursionError(msg)
output = output.invoke(
input_,
patch_config(
config,
callbacks=run_manager.get_child(),
recursion_limit=recursion_limit - 1,
),
)
return cast("Output", output)
async def _ainvoke(
self,
value: Input,
run_manager: AsyncCallbackManagerForChainRun,
config: RunnableConfig,
**kwargs: Any,
) -> Output:
if hasattr(self, "afunc"):View on GitHub (pinned to e32fa9a52e)
Solutions
- Break the cycle: ensure the delegated invocation consumes/transforms input so recursion terminates (e.g. return `output.invoke(x)` result, not the runnable chain itself).
- Raise the limit if the depth is legitimate: `runnable.invoke(x, config={'recursion_limit': 100})`.
- Restructure mutual delegation into an explicit loop or `RunnableSequence` instead of nested returns.
- Debug by printing the lambda inputs at each level to detect non-terminating delegation.
Example fix
// before step = RunnableLambda(lambda x: x + 1) loop = RunnableLambda(lambda x: step) # returns a Runnable -> infinite delegation // after step = RunnableLambda(lambda x: x + 1) loop = RunnableLambda(lambda x: step.invoke(x, config) if isinstance(x, int) else x) // better: express the pipeline directly chain = step | step | step
Defensive patterns
Strategy: validation
Validate before calling
def invoke_bounded(chain, x, max_depth: int = 50):
return chain.invoke(x, config={'recursion_limit': max_depth})
# and ensure lambdas never re-return a Runnable for identical input:
def terminates(fn, sample) -> bool:
out = fn(sample)
return not isinstance(out, type(None)) Try / catch
try:
out = chain.invoke(x)
except RecursionError as e:
if 'Recursion limit reached' in str(e):
out = chain.invoke(x, config={'recursion_limit': 100}) # only if depth is legitimate
else:
raise Prevention
- Never write a lambda that returns a Runnable for the same input it received.
- Absorb delegated runnable calls inside the lambda body.
- Set recursion_limit explicitly when deep delegation is by design.
- Add termination tests for router-style lambdas.
When it happens
Trigger: A lambda that returns a runnable chosen dynamically (`RunnableLambda(lambda x: route(x).invoke(...)` style self-delegation without consuming input); mutually delegating runnables; `config={'recursion_limit': 0 or small N}` with N levels of nested runnable-returning lambdas; long agent loops composed of `RunnableLambda`s.
Common situations: Building routers/agents where each lambda returns the next runnable and input never changes, creating an infinite chain; legitimately deep pipelines (dozens of nested runnable returns) hitting the default limit (25); users lowering `recursion_limit` to fail fast.
Related errors
- Recursion limit reached when invoking {self} with input {val
- Recursion limit reached when invoking {self} with input {fin
- Func was provided as a coroutine function, but afunc was als
- Expected a callable type for `func`.Instead got an unsupport
- Cannot invoke a coroutine function synchronously.Use `ainvok
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/54fa20b362a4ebdb.
Report an issue: GitHub.