langchain-ai/langchain · error · ValueError
Expected str, BaseMessage, list[BaseMessage], or tuple[BaseM
Error message
Expected str, BaseMessage, list[BaseMessage], or tuple[BaseMessage]. Got {input_val}. What it means
_get_input_messages accepts str, BaseMessage, list/tuple of messages, or a single-element list-of-list (batch of one). Anything else — int, dict, None — reaches the final raise. The wrapper uses this to normalize chain input into messages before persisting history, so the input at input_messages_key must be message-shaped.
Source
Thrown at libs/core/langchain_core/runnables/history.py:488
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:
key = "output"
# If you are wrapping a chat model directly
# The output is actually this weird generations object
if key not in output_val and "generations" in output_val:
output_val = output_val["generations"][0][0]["message"]
else:
output_val = output_val[key]View on GitHub (pinned to e32fa9a52e)
Solutions
- If the chain input is a dict, pass input_messages_key="<the key holding the message/string>" to RunnableWithMessageHistory
- Ensure the value at the input key is a str, BaseMessage, or sequence of BaseMessage
- Transform input upstream (e.g. wrap with a RunnableLambda mapping your schema to a message) before the history wrapper
Example fix
# before
wrapped = RunnableWithMessageHistory(chain, get_history) # chain input is {"question": str}
wrapped.invoke({"question": "hi", "context": 1}, cfg) # ValueError
# after
wrapped = RunnableWithMessageHistory(
chain, get_history, input_messages_key="question"
) Defensive patterns
Strategy: type-guard
Validate before calling
from langchain_core.messages import BaseMessage value = payload if input_messages_key is None else payload[input_messages_key] assert isinstance(value, (str, BaseMessage, list, tuple)), type(value)
Type guard
from langchain_core.messages import BaseMessage
def is_message_like(v: object) -> bool:
return isinstance(v, (str, BaseMessage, list, tuple)) Try / catch
try:
wrapped.invoke(payload, cfg)
except ValueError as e:
if "Expected str, BaseMessage" in str(e):
wrapped.invoke({"input": str(payload)}, cfg) Prevention
- Always set input_messages_key when the wrapped chain takes a dict
- Keep the message-bearing field a plain str or BaseMessage sequence
When it happens
Trigger: Invoking a RunnableWithMessageHistory chain whose input value (or value at input_messages_key) is not a str/BaseMessage/list/tuple, e.g. .invoke(123) or a dict payload without setting input_messages_key.
Common situations: Wrapping a chain whose input schema is a structured dict while forgetting input_messages_key; passing raw non-message data; the wrapped runnable returning/expecting custom types.
Related errors
- Expected a single list of messages. Got {input_val}.
- 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
- The input to RunnablePassthrough.assign() must be a dict.
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/5dcb16595605239d.
Report an issue: GitHub.