langchain-ai/langchain · error · ValueError
Expected a single list of messages. Got {input_val}.
Error message
Expected a single list of messages. Got {input_val}. What it means
RunnableWithMessageHistory._get_input_messages handles batched chat-model inputs: when the input is a list whose first element is itself a list, it assumes a batch of exactly one and returns that inner list. If there are two or more inner lists it cannot unambiguously pick one, so it raises this ValueError.
Source
Thrown at libs/core/langchain_core/runnables/history.py:481
input_val = input_val[key]
# If value is a string, convert to a human message
if isinstance(input_val, str):
return [HumanMessage(content=input_val)]
# If value is a single message, convert to a list
if isinstance(input_val, BaseMessage):
return [input_val]
# If value is a list or tuple...
if isinstance(input_val, (list, tuple)):
# Handle empty case
if len(input_val) == 0:
return list(input_val)
# If is a list of list, then return the first value
# This occurs for chat models - since we batch inputs
if isinstance(input_val[0], list):
if len(input_val) != 1:
msg = f"Expected a single list of messages. Got {input_val}."
raise ValueError(msg)
return input_val[0]
return list(input_val)
msg = (
f"Expected str, BaseMessage, list[BaseMessage], or tuple[BaseMessage]. "
f"Got {input_val}."
)
raise ValueError(msg)
def _get_output_messages(
self, output_val: str | BaseMessage | Sequence[BaseMessage] | dict[str, Any]
) -> list[BaseMessage]:
# If dictionary, try to pluck the single key representing messages
if isinstance(output_val, dict):
if self.output_messages_key:
key = self.output_messages_key
elif len(output_val) == 1:
key = next(iter(output_val.keys()))
else:View on GitHub (pinned to e32fa9a52e)
Solutions
- Call .batch([...]) on the wrapped chain so each item is routed separately, instead of .invoke([[...], [...]])
- Invoke one message list at a time: chain.invoke([msg], config)
- If input is a single list of messages, ensure elements are BaseMessage objects, not nested lists
Example fix
# before wrapped.invoke([[m1], [m2]], config) # ValueError # after wrapped.batch([[m1], [m2]], [config, config]) # or one at a time wrapped.invoke([m1], config)
Defensive patterns
Strategy: type-guard
Validate before calling
msgs = chain_input if isinstance(chain_input, list) else [chain_input]
if msgs and isinstance(msgs[0], list):
assert len(msgs) == 1, "invoke one batch element at a time, or use .batch()" Type guard
from langchain_core.messages import BaseMessage
def is_single_message_batch(v: object) -> bool:
return isinstance(v, list) and (not v or isinstance(v[0], BaseMessage)) Try / catch
try:
out = wrapped.invoke(user_input, cfg)
except ValueError as e:
if "single list of messages" in str(e):
outs = wrapped.batch(user_input, [cfg] * len(user_input)) Prevention
- Use .batch() for multiple inputs on history-wrapped chains
- Keep .invoke() inputs to a single str/BaseMessage/list[BaseMessage]
When it happens
Trigger: Invoking a RunnableWithMessageHistory-wrapped chain with a batch like [[msg1], [msg2]] passed to .invoke()/.stream() instead of .batch(), so the wrapper sees multiple message lists.
Common situations: Reusing a chat-model-style input shape (list of lists) with the history wrapper; migrating code from direct chat model calls; feeding batch payloads through .invoke().
Related errors
- Expected str, BaseMessage, list[BaseMessage], or tuple[BaseM
- Expected str, BaseMessage, list[BaseMessage], or tuple[BaseM
- Missing keys {sorted(missing_keys)} in config['configurable'
- Expected keys {sorted(expected_keys)} do not match parameter
- Argument 'prompts' is expected to be of type list[str], rece
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/fb26438e0ab79023.
Report an issue: GitHub.