{"record":{"id":"f82ffa108ff5910b","repo":"langchain-ai/langchain","slug":"expected-dict-to-have-exact-keys-role-and-conte","errorCode":null,"errorMessage":"Expected dict to have exact keys 'role' and 'content'. Got: {message}","messagePattern":"Expected dict to have exact keys 'role' and 'content'\\. Got: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/prompts/chat.py","lineNumber":1460,"sourceCode":"    \"\"\"\n    if isinstance(message, (BaseMessagePromptTemplate, BaseChatPromptTemplate)):\n        message_: BaseMessage | BaseMessagePromptTemplate | BaseChatPromptTemplate = (\n            message\n        )\n    elif isinstance(message, BaseMessage):\n        message_ = message\n    elif isinstance(message, str):\n        message_ = _create_template_from_message_type(\n            \"human\", message, template_format=template_format\n        )\n    elif isinstance(message, (tuple, dict)):\n        if isinstance(message, dict):\n            if set(message.keys()) != {\"content\", \"role\"}:\n                msg = (\n                    \"Expected dict to have exact keys 'role' and 'content'.\"\n                    f\" Got: {message}\"\n                )\n                raise ValueError(msg)\n            message_type_str = message[\"role\"]\n            template = message[\"content\"]\n        else:\n            if len(message) != 2:  # noqa: PLR2004\n                msg = f\"Expected 2-tuple of (role, template), got {message}\"  # type: ignore[unreachable]\n                raise ValueError(msg)\n            message_type_str, template = message\n\n        if isinstance(message_type_str, str):\n            message_ = _create_template_from_message_type(\n                message_type_str, template, template_format=template_format\n            )\n        elif (\n            hasattr(message_type_str, \"model_fields\")\n            and \"type\" in message_type_str.model_fields\n        ):\n            message_type = message_type_str.model_fields[\"type\"].default\n            message_ = _create_template_from_message_type(","sourceCodeStart":1442,"sourceCodeEnd":1478,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/prompts/chat.py#L1442-L1478","documentation":"Raised in _convert_to_message_template when a dict message passed to from_messages does not have exactly the two keys 'role' and 'content'. Extra keys (e.g. 'name', 'metadata') or missing/rename keys (e.g. 'text' instead of 'content') both trigger it.","triggerScenarios":"`ChatPromptTemplate.from_messages([{\"role\": \"user\", \"content\": \"hi\", \"name\": \"bob\"}])` (extra key), or `{\"role\": \"user\", \"text\": \"hi\"}` (wrong key). The comparison `set(message.keys()) != {\"content\", \"role\"}` is exact.","commonSituations":"Feeding OpenAI/Anthropic API-style message dicts (which legitimately carry extra fields like 'name' or 'tool_calls') straight into from_messages; renaming keys during refactors.","solutions":["Reduce each dict to exactly {\"role\": ..., \"content\": ...} before passing: {k: m[k] for k in (\"role\", \"content\")}.","For richer messages, construct the typed class instead: HumanMessage(content=\"hi\", name=\"bob\").","If the extra data matters (tool calls, names), use the appropriate message object rather than the dict shorthand."],"exampleFix":"# before\nChatPromptTemplate.from_messages([\n    {\"role\": \"user\", \"content\": \"hi\", \"name\": \"bob\"}\n])\n\n# after\nChatPromptTemplate.from_messages([\n    {\"role\": \"user\", \"content\": \"hi\"}\n])","handlingStrategy":"validation","validationCode":"def slim_message_dict(m):\n    if set(m.keys()) != {\"role\", \"content\"}:\n        return {\"role\": m[\"role\"], \"content\": m[\"content\"]}\n    return m","typeGuard":"def is_valid_message_dict(m: dict) -> bool:\n    return set(m.keys()) == {\"role\", \"content\"}","tryCatchPattern":null,"preventionTips":["Project API-style message dicts down to (role, content) before from_messages.","Use typed message classes when extra fields (name, tool_calls) must be preserved."],"tags":["prompts","from-messages","dict-messages"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}