langchain-ai/langchain · error · TypeError

RunnableBranch branches must be tuples or lists, not {type(b

Error message

RunnableBranch branches must be tuples or lists, not {type(branch)}

What it means

Each non-final argument to `RunnableBranch.__init__` must be a `tuple` or `list` of the form `(condition, runnable)`. When an argument is any other type (a bare function, a string, a dict, None), the `isinstance(branch, (tuple, list))` check fails and a `TypeError` naming the offending type is raised.

Source

Thrown at libs/core/langchain_core/runnables/branch.py:120

        if not isinstance(
            default,
            (Runnable, Callable, Mapping),  # type: ignore[arg-type]
        ):
            msg = "RunnableBranch default must be Runnable, callable or mapping."
            raise TypeError(msg)

        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(

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Wrap every condition branch as a 2-tuple: `RunnableBranch((is_q, answer_fn), default)`.
  2. When assembling branches programmatically, validate each element is a tuple/list of length 2 before constructing.
  3. Check for None/empty elements in generated branch lists and filter them out.

Example fix

# before
branch = RunnableBranch(is_q, answer_fn, default_fn)

# after
branch = RunnableBranch((is_q, answer_fn), default_fn)
Defensive patterns

Strategy: validation

Validate before calling

for b in branches[:-1]:
    assert isinstance(b, (tuple, list)), f"branch must be tuple/list, got {type(b)}: {b!r}"

Type guard

from typing import TypeGuard

def is_branch_pair(b: object) -> TypeGuard[tuple | list]:
    return isinstance(b, (tuple, list))

Prevention

When it happens

Trigger: Passing unwrapped callables as condition branches: `RunnableBranch(is_q, answer_fn, default)`; passing a dict `{cond: fn}` instead of a pair; forgetting the parentheses around a pair; passing None from a failed lookup in a dynamically built branch list.

Common situations: Porting code from LCEL pipe syntax where bare functions are valid; building branches with `*branches` where one element is malformed; assuming keyword-style `{condition: runnable}` dicts are accepted like in older examples.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/e27a53d9baaabbc4. Report an issue: GitHub.