langchain-ai/langchain · error · TypeError

Expected a Runnable, callable or dict.Instead got an unsuppo

Error message

Expected a Runnable, callable or dict.Instead got an unsupported type: {type(thing)}

What it means

Raised by `coerce_to_runnable` in `langchain_core.runnables.base` when the value passed cannot be converted into a `Runnable`. The function only accepts a `Runnable`, an (async) generator function, any other callable (wrapped in `RunnableLambda`), or a dict (wrapped in `RunnableParallel`). Any other type (str, int, list, None, a generator object rather than a generator function, etc.) reaches the fallback `raise TypeError`.

Source

Thrown at libs/core/langchain_core/runnables/base.py:6652

    Returns:
        A `Runnable`.

    Raises:
        TypeError: If the object is not `Runnable`-like.
    """
    if isinstance(thing, Runnable):
        return thing
    if is_async_generator(thing) or inspect.isgeneratorfunction(thing):
        return RunnableGenerator(thing)
    if callable(thing):
        return RunnableLambda(cast("Callable[[Input], Output]", thing))
    if isinstance(thing, dict):
        return RunnableParallel(thing)
    msg = (
        f"Expected a Runnable, callable or dict."
        f"Instead got an unsupported type: {type(thing)}"
    )
    raise TypeError(msg)


@overload
def chain(
    func: Callable[[Input], Coroutine[Any, Any, Output]],
) -> Runnable[Input, Output]: ...


@overload
def chain(
    func: Callable[[Input], Iterator[Output]],
) -> Runnable[Input, Output]: ...


@overload
def chain(
    func: Callable[[Input], AsyncIterator[Output]],
) -> Runnable[Input, Output]: ...

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Wrap the value in `RunnableLambda`: `RunnableLambda(value)` if it is a function-like object, or `RunnableLambda(lambda _: value)` for constants.
  2. If the value is a static dict of runnables, pass the dict directly so it becomes a `RunnableParallel`.
  3. Import the actual function/class instead of passing its name or module path as a string.
  4. For generator-based streaming steps, pass the generator function (`def gen(x): yield ...`), not the result of calling it.
  5. Check for None before building the chain when the value comes from an optional config or lookup.

Example fix

// before
branch = RunnableBranch((is_q, "answer"), default_fn)

// after
from langchain_core.runnables import RunnableLambda
branch = RunnableBranch((is_q, RunnableLambda(lambda _: "answer")), default_fn)
Defensive patterns

Strategy: type-guard

Validate before calling

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

def is_coercible(thing) -> bool:
    return (
        isinstance(thing, (Runnable, Mapping))
        or callable(thing)
        or inspect.isgeneratorfunction(thing)
    )

assert all(is_coercible(b) for b in branch_values), "non-coercible branch value"

Type guard

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

def is_runnable_coercible(thing: object) -> TypeGuard[Runnable | Callable | Mapping]:
    return (
        isinstance(thing, (Runnable, Mapping))
        or callable(thing)
        or inspect.isgeneratorfunction(thing)
    )

Try / catch

try:
    runnable = coerce_to_runnable(thing)
except TypeError as e:
    raise ValueError(f"Bad chain step {thing!r}: wrap callables/consts in RunnableLambda") from e

Prevention

When it happens

Trigger: Passing a non-callable, non-dict object anywhere a Runnable is coerced: a `RunnableBranch` condition or branch runnable, `RunnableParallel` values, sequence steps built via `RunnableSequence`/`|`, or `RunnableWithFallbacks(fallbacks=[...])` with a raw string/int/None in the list. Also passing `some_generator()` (an already-consumed generator object) instead of the generator function itself.

Common situations: Building a branch or parallel chain from a plain string or constant, e.g. `RunnableBranch((cond, "done"), default_fn)`; putting a module path string like `"my.module.fn"` instead of the imported function; passing None from an optional lookup; passing a list like `[fn1, fn2]` where a dict `{...}` was intended.

Related errors


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