langchain-ai/langchain · error · ValueError

Expected dict to have exact keys 'role' and 'content'. Got:

Error message

Expected dict to have exact keys 'role' and 'content'. Got: {message}

What it means

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.

Source

Thrown at libs/core/langchain_core/prompts/chat.py:1460

    """
    if isinstance(message, (BaseMessagePromptTemplate, BaseChatPromptTemplate)):
        message_: BaseMessage | BaseMessagePromptTemplate | BaseChatPromptTemplate = (
            message
        )
    elif isinstance(message, BaseMessage):
        message_ = message
    elif isinstance(message, str):
        message_ = _create_template_from_message_type(
            "human", message, template_format=template_format
        )
    elif isinstance(message, (tuple, dict)):
        if isinstance(message, dict):
            if set(message.keys()) != {"content", "role"}:
                msg = (
                    "Expected dict to have exact keys 'role' and 'content'."
                    f" Got: {message}"
                )
                raise ValueError(msg)
            message_type_str = message["role"]
            template = message["content"]
        else:
            if len(message) != 2:  # noqa: PLR2004
                msg = f"Expected 2-tuple of (role, template), got {message}"  # type: ignore[unreachable]
                raise ValueError(msg)
            message_type_str, template = message

        if isinstance(message_type_str, str):
            message_ = _create_template_from_message_type(
                message_type_str, template, template_format=template_format
            )
        elif (
            hasattr(message_type_str, "model_fields")
            and "type" in message_type_str.model_fields
        ):
            message_type = message_type_str.model_fields["type"].default
            message_ = _create_template_from_message_type(

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Reduce each dict to exactly {"role": ..., "content": ...} before passing: {k: m[k] for k in ("role", "content")}.
  2. For richer messages, construct the typed class instead: HumanMessage(content="hi", name="bob").
  3. If the extra data matters (tool calls, names), use the appropriate message object rather than the dict shorthand.

Example fix

# before
ChatPromptTemplate.from_messages([
    {"role": "user", "content": "hi", "name": "bob"}
])

# after
ChatPromptTemplate.from_messages([
    {"role": "user", "content": "hi"}
])
Defensive patterns

Strategy: validation

Validate before calling

def slim_message_dict(m):
    if set(m.keys()) != {"role", "content"}:
        return {"role": m["role"], "content": m["content"]}
    return m

Type guard

def is_valid_message_dict(m: dict) -> bool:
    return set(m.keys()) == {"role", "content"}

Prevention

When it happens

Trigger: `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.

Common situations: 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.

Related errors


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