langchain-ai/langchain · error · ValueError

If 'exception_key' is specified then input must be a diction

Error message

If 'exception_key' is specified then input must be a dictionary.However found a type of {type(input)} for input

What it means

`RunnableWithFallbacks.invoke` requires that when `exception_key` is set (failures are recorded into the input dict under that key), the input must be a `dict`, because the mechanism writes the error back into the input before running fallbacks. A non-dict input fails the `isinstance(input, dict)` check and `ValueError` is raised before any runnable executes.

Source

Thrown at libs/core/langchain_core/runnables/fallbacks.py:173

    def runnables(self) -> Iterator[Runnable[Input, Output]]:
        """Iterator over the `Runnable` and its fallbacks.

        Yields:
            The `Runnable` then its fallbacks.
        """
        yield self.runnable
        yield from self.fallbacks

    @override
    def invoke(
        self, input: Input, config: RunnableConfig | None = None, **kwargs: Any
    ) -> Output:
        if self.exception_key is not None and not isinstance(input, dict):
            msg = (
                "If 'exception_key' is specified then input must be a dictionary."
                f"However found a type of {type(input)} for input"
            )
            raise ValueError(msg)
        # setup callbacks
        config = ensure_config(config)
        callback_manager = get_callback_manager_for_config(config)
        # start the root run
        run_manager = callback_manager.on_chain_start(
            None,
            input,
            name=config.get("run_name") or self.get_name(),
            run_id=config.pop("run_id", None),
        )
        first_error = None
        last_error = None
        for runnable in self.runnables:
            try:
                if self.exception_key and last_error is not None:
                    input[self.exception_key] = last_error  # type: ignore[index]
                child_config = patch_config(config, callbacks=run_manager.get_child())
                with set_config_context(child_config) as context:

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Remove `exception_key` if you do not need error details recorded in the input.
  2. Feed a dict: wrap the value as `{"input": value}` (or a meaningful key) before it reaches the fallback runnable, and update downstream prompts to read that key.
  3. If the input is a Pydantic object, pass `model.model_dump()`.

Example fix

# before
fb = parser.with_fallbacks([other_parser], exception_key="errors")
out = fb.invoke("some text")  # ValueError

# after
out = fb.invoke({"text": "some text", "errors": None})  # and read input["text"] inside
Defensive patterns

Strategy: validation

Validate before calling

if fb.exception_key is not None:
    assert isinstance(inputs_payload, dict), "exception_key requires dict input"
fb.invoke(inputs_payload)

Type guard

from typing import TypeGuard

def is_dict_input(x: object) -> TypeGuard[dict]:
    return isinstance(x, dict)

Prevention

When it happens

Trigger: Creating a fallback with `runnable.with_fallbacks(fallbacks, exception_key="errors")` and then invoking it with a string, list, or object input: `with_fallbacks.invoke("hello")`, or a chain whose preceding step emits a plain string (e.g. a `RunnableLambda` returning text) piped into the fallback-wrapped step.

Common situations: Wrapping a parser or model step in fallbacks and feeding it a raw string; converting an existing `.with_fallbacks(...)` chain to record errors via `exception_key` without changing the pipeline so the input stays a dict; passing a Pydantic model object instead of its `.model_dump()`.

Related errors


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