langchain-ai/langchain · error · ValueError
RunnableBranch branches must be tuples or lists of length 2,
Error message
RunnableBranch branches must be tuples or lists of length 2, not {len(branch)} What it means
Each `(condition, runnable)` pair passed to `RunnableBranch.__init__` must have exactly 2 elements (`_MIN_BRANCHES == 2`). A tuple or list of any other length (1, 3, ...) fails the `len(branch) != _MIN_BRANCHES` check and raises `ValueError` with the actual length.
Source
Thrown at libs/core/langchain_core/runnables/branch.py:127
default_ = coerce_to_runnable(cast("Runnable[Input, Output]", default))
branches_ = []
for branch in branches[:-1]:
if not isinstance(branch, (tuple, list)):
msg = (
f"RunnableBranch branches must be "
f"tuples or lists, not {type(branch)}"
)
raise TypeError(msg)
if len(branch) != _MIN_BRANCHES:
msg = (
f"RunnableBranch branches must be "
f"tuples or lists of length 2, not {len(branch)}"
)
raise ValueError(msg)
condition, runnable = branch
condition = cast("Runnable[Input, bool]", coerce_to_runnable(condition))
runnable = coerce_to_runnable(cast("Runnable[Input, Output]", runnable))
branches_.append((condition, runnable))
super().__init__(
branches=branches_,
default=default_,
)
model_config = ConfigDict(
arbitrary_types_allowed=True,
)
@classmethod
def is_lc_serializable(cls) -> bool:
"""Return `True` as this class is serializable."""
return TrueView on GitHub (pinned to e32fa9a52e)
Solutions
- Ensure each branch tuple is exactly `(condition, runnable)`.
- Move per-branch options off the tuple: apply `.with_config(tags=[...])` or `.with_retry()` to the runnable inside the pair.
- Log `[(type(b), len(b) if isinstance(b, (tuple, list)) else None) for b in branches]` before construction to find the malformed pair.
Example fix
# before
branch = RunnableBranch((is_q, answer_fn, {"tags": ["q"]}), default)
# after
branch = RunnableBranch((is_q, answer_fn.with_config(tags=["q"])), default) Defensive patterns
Strategy: validation
Validate before calling
bad = [b for b in branches if not (isinstance(b, (tuple, list)) and len(b) == 2)]
if bad:
raise ValueError(f"malformed branches (need length-2 pairs): {bad!r}") Type guard
from typing import TypeGuard
def is_length2_pair(b: object) -> TypeGuard[tuple]:
return isinstance(b, tuple) and len(b) == 2 Prevention
- Keep branch tuples exactly (condition, runnable).
- Apply per-branch options via .with_config()/.with_retry() on the runnable, not extra tuple elements.
- Watch for trailing commas that create 1-tuples.
When it happens
Trigger: Passing `(cond,)` (missing runnable), `(cond, fn, extra)` (extra element), or `(cond, fn, handler_kwargs)` where a config dict was mistakenly packed into the pair. Also destructuring bugs when branches are built with `zip()` of unequal lists.
Common situations: Trying to attach per-branch config/metadata as a third tuple element (not supported — use `.with_config()` on the runnable instead); building pairs with a loop bug that appends the wrong number of items; copy-paste leaving a trailing comma making a 1-tuple.
Related errors
- RunnableBranch requires at least two branches
- RunnableBranch default must be Runnable, callable or mapping
- RunnableBranch branches must be tuples or lists, not {type(b
- Expected a Runnable, callable or dict.Instead got an unsuppo
- length must be >= 0, but got {length}
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/abbaf021f44b635f.
Report an issue: GitHub.