langchain-ai/langchain · error · ValueError

MESSAGE_COERCION_FAILURE

MESSAGE_COERCION_FAILURE

Error message

Unexpected message type: '{message_type}'. Use one of 'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', or 'developer'.
For troubleshooting, visit: https://docs.langchain.com/oss/python/langchain/errors/MESSAGE_COERCION_FAILURE 

What it means

Raised by `_create_message_from_message_type` (reached via `convert_to_messages`) when the message-type discriminator string is not one of 'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', 'developer', or 'remove'. It carries the `MESSAGE_COERCION_FAILURE` error code and a troubleshooting URL.

Source

Thrown at libs/core/langchain_core/messages/utils.py:680

            kwargs["additional_kwargs"]["__openai_role__"] = "developer"
        message = SystemMessage(content=content, **kwargs)
    elif message_type == "function":
        message = FunctionMessage(content=content, **kwargs)
    elif message_type == "tool":
        artifact = kwargs.get("additional_kwargs", {}).pop("artifact", None)
        status = kwargs.get("additional_kwargs", {}).pop("status", None)
        if status is not None:
            kwargs["status"] = status
        message = ToolMessage(content=content, artifact=artifact, **kwargs)
    elif message_type == "remove":
        message = RemoveMessage(**kwargs)
    else:
        msg = (
            f"Unexpected message type: '{message_type}'. Use one of 'human',"
            f" 'user', 'ai', 'assistant', 'function', 'tool', 'system', or 'developer'."
        )
        msg = create_message(message=msg, error_code=ErrorCode.MESSAGE_COERCION_FAILURE)
        raise ValueError(msg)
    return message


# Map of class names emitted in the `Serializable` constructor-envelope
# (`{"lc": 1, "type": "constructor", "id": [..., "<ClassName>"],
# "kwargs": {...}}`) to the message-type strings
# `_create_message_from_message_type` accepts. Read by
# `_convert_to_message`'s dict branch when unpacking that wire shape.
# Kept as a hardcoded allowlist of strings rather than a class registry
# lookup so dispatch never resolves to a class chosen by the caller.
_LC_CONSTRUCTOR_NAME_TO_TYPE: dict[str, str] = {
    "HumanMessage": "human",
    "HumanMessageChunk": "human",
    "AIMessage": "ai",
    "AIMessageChunk": "ai",
    "SystemMessage": "system",
    "SystemMessageChunk": "system",
    "FunctionMessage": "function",

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Map the role string to a supported one before conversion (see the supported list in the error message)
  2. Check for stray whitespace/case: use lowercase exact strings
  3. For new provider roles like 'developer', upgrade langchain-core to a version that supports them

Example fix

# before
convert_to_messages([{'type': 'tool_message', 'content': '42'}])

# after
convert_to_messages([{'type': 'tool', 'content': '42', 'tool_call_id': 'call_1'}])
Defensive patterns

Strategy: validation

Validate before calling

VALID_ROLES = {'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', 'developer', 'remove'}

def coerce_role(role: str) -> str:
    aliases = {'tool_message': 'tool', 'bot': 'ai'}
    r = role.strip().lower()
    if r not in VALID_ROLES:
        r = aliases.get(r, r)
    assert r in VALID_ROLES, f'unknown role {role!r}'
    return r

Type guard

def is_valid_message_type(t: object) -> bool:
    return isinstance(t, str) and t.strip().lower() in {
        'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', 'developer', 'remove'}

Try / catch

try:
    msgs = convert_to_messages(raw)
except ValueError as e:
    if 'MESSAGE_COERCION_FAILURE' in str(e) and 'Unexpected message type' in str(e):
        raw = [(coerce_role(r), c) for r, c in raw]
        msgs = convert_to_messages(raw)
    else:
        raise

Prevention

When it happens

Trigger: Passing tuples/dicts with an unknown role string, e.g. `('agent', 'hi')`, `{'type': 'bot', 'content': 'hi'}`, or an OpenAI role like 'tool_message' instead of 'tool'.

Common situations: Converting provider-specific histories (Bedrock/Anthropic/Gemini role names) without mapping to langchain roles; typos like 'assistance' or 'system '; roles added by a provider that langchain-core has not aliased yet.

Related errors


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