{"record":{"id":"5dcb16595605239d","repo":"langchain-ai/langchain","slug":"expected-str-basemessage-list-basemessage-or-t","errorCode":null,"errorMessage":"Expected str, BaseMessage, list[BaseMessage], or tuple[BaseMessage]. Got {input_val}.","messagePattern":"Expected str, BaseMessage, list\\[BaseMessage\\], or tuple\\[BaseMessage\\]\\. Got (.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/runnables/history.py","lineNumber":488,"sourceCode":"            return [input_val]\n        # If value is a list or tuple...\n        if isinstance(input_val, (list, tuple)):\n            # Handle empty case\n            if len(input_val) == 0:\n                return list(input_val)\n            # If is a list of list, then return the first value\n            # This occurs for chat models - since we batch inputs\n            if isinstance(input_val[0], list):\n                if len(input_val) != 1:\n                    msg = f\"Expected a single list of messages. Got {input_val}.\"\n                    raise ValueError(msg)\n                return input_val[0]\n            return list(input_val)\n        msg = (\n            f\"Expected str, BaseMessage, list[BaseMessage], or tuple[BaseMessage]. \"\n            f\"Got {input_val}.\"\n        )\n        raise ValueError(msg)\n\n    def _get_output_messages(\n        self, output_val: str | BaseMessage | Sequence[BaseMessage] | dict[str, Any]\n    ) -> list[BaseMessage]:\n        # If dictionary, try to pluck the single key representing messages\n        if isinstance(output_val, dict):\n            if self.output_messages_key:\n                key = self.output_messages_key\n            elif len(output_val) == 1:\n                key = next(iter(output_val.keys()))\n            else:\n                key = \"output\"\n            # If you are wrapping a chat model directly\n            # The output is actually this weird generations object\n            if key not in output_val and \"generations\" in output_val:\n                output_val = output_val[\"generations\"][0][0][\"message\"]\n            else:\n                output_val = output_val[key]","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/runnables/history.py#L470-L506","documentation":"_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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nwrapped = RunnableWithMessageHistory(chain, get_history)  # chain input is {\"question\": str}\nwrapped.invoke({\"question\": \"hi\", \"context\": 1}, cfg)  # ValueError\n# after\nwrapped = RunnableWithMessageHistory(\n    chain, get_history, input_messages_key=\"question\"\n)","handlingStrategy":"type-guard","validationCode":"from langchain_core.messages import BaseMessage\nvalue = payload if input_messages_key is None else payload[input_messages_key]\nassert isinstance(value, (str, BaseMessage, list, tuple)), type(value)","typeGuard":"from langchain_core.messages import BaseMessage\n\ndef is_message_like(v: object) -> bool:\n    return isinstance(v, (str, BaseMessage, list, tuple))","tryCatchPattern":"try:\n    wrapped.invoke(payload, cfg)\nexcept ValueError as e:\n    if \"Expected str, BaseMessage\" in str(e):\n        wrapped.invoke({\"input\": str(payload)}, cfg)","preventionTips":["Always set input_messages_key when the wrapped chain takes a dict","Keep the message-bearing field a plain str or BaseMessage sequence"],"tags":["runnable","message-history","input-validation","valueerror"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}