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
- Wrap the value in `RunnableLambda`: `RunnableLambda(value)` if it is a function-like object, or `RunnableLambda(lambda _: value)` for constants.
- If the value is a static dict of runnables, pass the dict directly so it becomes a `RunnableParallel`.
- Import the actual function/class instead of passing its name or module path as a string.
- For generator-based streaming steps, pass the generator function (`def gen(x): yield ...`), not the result of calling it.
- 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
- Wrap constants in RunnableLambda(lambda _: value) at construction time.
- Never pass module-path strings; import the function.
- Pass generator functions, not generator objects.
- Add unit tests asserting each dynamic chain step is a Runnable, callable, or dict.
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
- RunnableBranch default must be Runnable, callable or mapping
- RunnableBranch branches must be tuples or lists, not {type(b
- unsupported operand type(s) for +: "{self.__class__.__name__
- RunnableBranch requires at least two branches
- RunnableBranch branches must be tuples or lists of length 2,
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/7ebf36c3c271be08.
Report an issue: GitHub.