langchain-ai/langchain · error · TypeError

RunnableBranch default must be Runnable, callable or mapping

Error message

RunnableBranch default must be Runnable, callable or mapping.

What it means

In `RunnableBranch.__init__`, the last positional argument is the default branch and must be a `Runnable`, a `Callable`, or a `Mapping` so it can be coerced via `coerce_to_runnable`. Anything else (str, int, None, list, tuple) fails this `isinstance` check and raises `TypeError`.

Source

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

        Raises:
            ValueError: If the number of branches is less than `2`.
            TypeError: If the default branch is not `Runnable`, `Callable` or `Mapping`.
            TypeError: If a branch is not a `tuple` or `list`.
            ValueError: If a branch is not of length `2`.
        """
        if len(branches) < _MIN_BRANCHES:
            msg = "RunnableBranch requires at least two branches"
            raise ValueError(msg)

        default = branches[-1]

        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)}"
                )

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Wrap the final value: `RunnableBranch((cond, fn), RunnableLambda(lambda _: "default"))`.
  2. If the default is a sequence of steps, use a `RunnableSequence`/pipe chain: `(prompt | model | parser)`.
  3. If the default should be a set of parallel steps, pass a dict (Mapping) of runnables.

Example fix

# before
branch = RunnableBranch((cond, fn), "fallback")

# after
from langchain_core.runnables import RunnableLambda
branch = RunnableBranch((cond, fn), RunnableLambda(lambda _: "fallback"))
Defensive patterns

Strategy: type-guard

Validate before calling

from collections.abc import Callable, Mapping
from langchain_core.runnables import Runnable

ok = isinstance(default, (Runnable, Callable, Mapping))
assert ok, f"default must be Runnable/callable/mapping, got {type(default)}"

Type guard

from collections.abc import Callable, Mapping
from typing import TypeGuard
from langchain_core.runnables import Runnable

def is_valid_default(d: object) -> TypeGuard[Runnable | Callable | Mapping]:
    return isinstance(d, (Runnable, Callable, Mapping))

Prevention

When it happens

Trigger: Ending the `RunnableBranch(...)` argument list with a non-runnable value: `RunnableBranch((cond, fn), "default")`, `RunnableBranch((cond, fn), None)`, or `RunnableBranch((cond, fn), [fn1, fn2])` (a list, not a Mapping/Runnable).

Common situations: Using a literal string as the fallback answer; passing a list of runnables expecting them to run in sequence instead of a `RunnableSequence`; a variable that is None because an optional handler was not configured; forgetting that the final argument is the default, not another condition pair.

Related errors


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