langchain-ai/langchain · error · ValueError

If 'exception_key' is specified then inputs must be dictiona

Error message

If 'exception_key' is specified then inputs must be dictionaries.However found a type of {type(inputs[0])} for input

What it means

`RunnableWithFallbacks.batch` validates every element of `inputs` is a `dict` when `exception_key` is set, because each failing run's error is written into its corresponding input dict for the fallbacks to see. If any element is not a dict, `ValueError` is raised before batching starts; the message reports the type of the first element.

Source

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

        raise first_error

    @override
    def batch(
        self,
        inputs: list[Input],
        config: RunnableConfig | list[RunnableConfig] | None = None,
        *,
        return_exceptions: bool = False,
        **kwargs: Any | None,
    ) -> list[Output]:
        if self.exception_key is not None and not all(
            isinstance(input_, dict) for input_ in inputs
        ):
            msg = (
                "If 'exception_key' is specified then inputs must be dictionaries."
                f"However found a type of {type(inputs[0])} for input"
            )
            raise ValueError(msg)

        if not inputs:
            return []

        # setup callbacks
        configs = get_config_list(config, len(inputs))
        callback_managers = [
            CallbackManager.configure(
                inheritable_callbacks=config.get("callbacks"),
                local_callbacks=None,
                verbose=False,
                inheritable_tags=config.get("tags"),
                local_tags=None,
                inheritable_metadata=config.get("metadata"),
                local_metadata=None,
            )
            for config in configs
        ]

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Normalize every input to a dict before batching: `[x if isinstance(x, dict) else {"text": x} for x in inputs]`.
  2. Remove `exception_key` from `with_fallbacks` if per-item error capture is unnecessary.
  3. Add a validation pass that rejects/logs non-dict entries at ingestion time.

Example fix

# before
outs = fb.batch(prompts)  # prompts: list[str], fb has exception_key

# after
outs = fb.batch([{"text": p} for p in prompts])
Defensive patterns

Strategy: validation

Validate before calling

inputs = [x if isinstance(x, dict) else {"text": x} for x in inputs]
fb.batch(inputs)

Type guard

from typing import TypeGuard

def all_dicts(xs: list[object]) -> TypeGuard[list[dict]]:
    return all(isinstance(x, dict) for x in xs)

Prevention

When it happens

Trigger: `fb.batch(["a", "b"])` or `fb.batch([dict_input, string_input])` on a runnable created with `.with_fallbacks(..., exception_key="errors")`; mixing formats when batching heterogeneous requests; a producer that sometimes yields strings instead of payload dicts.

Common situations: Batching prompts as strings with a fallback-wrapped parser; a queue consumer where some messages are already dicts and others are raw text; reusing the `exception_key` feature with legacy string inputs after upgrading the pipeline.

Related errors


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