langchain-ai/langchain · error · ValueError

Expected str, BaseMessage, list[BaseMessage], or tuple[BaseM

Error message

Expected str, BaseMessage, list[BaseMessage], or tuple[BaseMessage]. Got {output_val}.

What it means

The mirror of the input check for outputs: _get_output_messages must turn the wrapped chain's output into messages. Dicts are handled by plucking output_messages_key (or the single key, or "output"), but after that the value must be str, BaseMessage, or list/tuple. Anything else raises this ValueError.

Source

Thrown at libs/core/langchain_core/runnables/history.py:519

            # 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]

        if isinstance(output_val, str):
            return [AIMessage(content=output_val)]
        # If value is a single message, convert to a list
        if isinstance(output_val, BaseMessage):
            return [output_val]
        if isinstance(output_val, (list, tuple)):
            return list(output_val)
        msg = (
            f"Expected str, BaseMessage, list[BaseMessage], or tuple[BaseMessage]. "
            f"Got {output_val}."
        )
        raise ValueError(msg)

    def _enter_history(self, value: Any, config: RunnableConfig) -> list[BaseMessage]:
        hist: BaseChatMessageHistory = config["configurable"]["message_history"]
        messages = hist.messages.copy()

        if not self.history_messages_key:
            # return all messages
            input_val = (
                value if not self.input_messages_key else value[self.input_messages_key]
            )
            messages += self._get_input_messages(input_val)
        return messages

    async def _aenter_history(
        self, value: dict[str, Any], config: RunnableConfig
    ) -> list[BaseMessage]:
        hist: BaseChatMessageHistory = config["configurable"]["message_history"]
        messages = (await hist.aget_messages()).copy()

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Pass output_messages_key pointing at the key that holds the str/BaseMessage/list[BaseMessage], e.g. output_messages_key="answer"
  2. Add a final step to the wrapped chain that maps its output to a message (e.g. itemgetter("answer") or a small RunnableLambda)
  3. Ensure the value at that key is a string or message objects, not arbitrary JSON

Example fix

# before
wrapped = RunnableWithMessageHistory(chain, get_history)
# chain outputs {"answer": "...", "citations": [...]} -> ValueError or wrong key
# after
wrapped = RunnableWithMessageHistory(
    chain, get_history, output_messages_key="answer"
)
Defensive patterns

Strategy: type-guard

Validate before calling

# dry-run the inner chain once and inspect output shape
sample = chain.invoke(sample_input)
if isinstance(sample, dict):
    assert len(sample) == 1 or "output" in sample or output_messages_key, sample.keys()

Type guard

from langchain_core.messages import BaseMessage

def is_message_output(v: object) -> bool:
    return isinstance(v, (str, BaseMessage, list, tuple)) or isinstance(v, dict)

Try / catch

try:
    wrapped.invoke(payload, cfg)
except ValueError as e:
    if "Expected str, BaseMessage" in str(e) and "output_val" in str(e):
        wrapped2 = RunnableWithMessageHistory(
            chain, get_history, output_messages_key="answer"
        )

Prevention

When it happens

Trigger: A wrapped chain whose output dict value at the chosen key is a non-message type (e.g. an int or nested dict), or an output that is not str/BaseMessage/list at all and not a dict; no output_messages_key configured.

Common situations: Structured-output chains returning nested JSON under a key; multi-key output dicts where the message lives under a key other than the single key or "output"; retriever-style outputs of custom types.

Related errors


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