langchain-ai/langchain · error · ValueError

Unexpected message type: {message_type}. Use one of 'human',

Error message

Unexpected message type: {message_type}. Use one of 'human', 'user', 'ai', 'assistant', or 'system'.

What it means

Raised in _create_template_from_message_type when the message role string is not one of the recognized types: 'human'/'user', 'ai'/'assistant', 'system', or 'placeholder'. The role comes from the first element of a (role, template) tuple or the 'role' key of a dict message.

Source

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

            if not isinstance(var_name_wrapped, str):
                msg = f"Expected variable name to be a string. Got: {var_name_wrapped}"
                raise ValueError(msg)  # noqa: TRY004
            if var_name_wrapped[0] != "{" or var_name_wrapped[-1] != "}":
                msg = (
                    f"Invalid placeholder template: {var_name_wrapped}."
                    " Expected a variable name surrounded by curly braces."
                )
                raise ValueError(msg)
            var_name = var_name_wrapped[1:-1]

            message = MessagesPlaceholder(variable_name=var_name, optional=is_optional)
    else:
        msg = (
            f"Unexpected message type: {message_type}. Use one of 'human',"
            f" 'user', 'ai', 'assistant', or 'system'."
        )
        raise ValueError(msg)
    return message


def _convert_to_message_template(
    message: MessageLikeRepresentation,
    template_format: PromptTemplateFormat = "f-string",
) -> BaseMessage | BaseMessagePromptTemplate | BaseChatPromptTemplate:
    """Instantiate a message from a variety of message formats.

    A message can be represented using the following formats:

    1. `BaseMessagePromptTemplate`
    2. `BaseMessage`
    3. 2-tuple of `(message type, template)`; e.g., `('human', '{user_input}')`
    4. 2-tuple of `(message class, template)`
    5. A string which is shorthand for `('human', template)`; e.g., `'{user_input}'`

    Args:

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Map the role to an accepted one: 'bot'->'ai', 'user'->'human', 'assistant'->'ai'.
  2. For tool/function messages, pass the actual message class (ToolMessage(...)) instead of a role tuple.
  3. Normalize role strings to lowercase and validate against the accepted set before building the prompt.

Example fix

# before
ChatPromptTemplate.from_messages([("bot", "hi")])

# after
ChatPromptTemplate.from_messages([("ai", "hi")])
Defensive patterns

Strategy: validation

Validate before calling

ACCEPTED_ROLES = {"human", "user", "ai", "assistant", "system", "placeholder"}

def normalize_role(role):
    r = role.lower()
    aliases = {"bot": "ai", "model": "ai", "tool": None}
    return aliases.get(r, r if r in ACCEPTED_ROLES else None)

Type guard

def is_supported_role(role: str) -> bool:
    return role in {"human", "user", "ai", "assistant", "system", "placeholder"}

Try / catch

try:
    prompt = ChatPromptTemplate.from_messages(messages)
except ValueError as e:
    if "Unexpected message type" in str(e):
        # log offending role and map/fix it
        raise
    raise

Prevention

When it happens

Trigger: `ChatPromptTemplate.from_messages([("bot", "hi")])`, `[{"role": "tool", "content": ...}]`, or a typo like "System" (capitalized) or "aı" — none are mapped. Note 'tool' and 'placeholder' handling differs: only the listed roles are accepted here.

Common situations: Porting OpenAI-style message lists (which allow 'tool' and 'function' roles) into from_messages; typos and case mismatches; role strings sourced from user input or a database.

Related errors


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