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

  1. If the chain input is a dict, pass input_messages_key="<the key holding the message/string>" to RunnableWithMessageHistory
  2. Ensure the value at the input key is a str, BaseMessage, or sequence of BaseMessage
  3. 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

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


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