{"record":{"id":"2d527ad476a16094","repo":"langchain-ai/langchain","slug":"message-coercion-failure","errorCode":"MESSAGE_COERCION_FAILURE","errorMessage":"Unexpected message type: '{message_type}'. Use one of 'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', or 'developer'.\nFor troubleshooting, visit: https://docs.langchain.com/oss/python/langchain/errors/MESSAGE_COERCION_FAILURE ","messagePattern":"Unexpected message type: '(.+?)'\\. Use one of 'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', or 'developer'\\.\nFor troubleshooting, visit: https://docs\\.langchain\\.com/oss/python/langchain/errors/MESSAGE_COERCION_FAILURE ","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/messages/utils.py","lineNumber":680,"sourceCode":"            kwargs[\"additional_kwargs\"][\"__openai_role__\"] = \"developer\"\n        message = SystemMessage(content=content, **kwargs)\n    elif message_type == \"function\":\n        message = FunctionMessage(content=content, **kwargs)\n    elif message_type == \"tool\":\n        artifact = kwargs.get(\"additional_kwargs\", {}).pop(\"artifact\", None)\n        status = kwargs.get(\"additional_kwargs\", {}).pop(\"status\", None)\n        if status is not None:\n            kwargs[\"status\"] = status\n        message = ToolMessage(content=content, artifact=artifact, **kwargs)\n    elif message_type == \"remove\":\n        message = RemoveMessage(**kwargs)\n    else:\n        msg = (\n            f\"Unexpected message type: '{message_type}'. Use one of 'human',\"\n            f\" 'user', 'ai', 'assistant', 'function', 'tool', 'system', or 'developer'.\"\n        )\n        msg = create_message(message=msg, error_code=ErrorCode.MESSAGE_COERCION_FAILURE)\n        raise ValueError(msg)\n    return message\n\n\n# Map of class names emitted in the `Serializable` constructor-envelope\n# (`{\"lc\": 1, \"type\": \"constructor\", \"id\": [..., \"<ClassName>\"],\n# \"kwargs\": {...}}`) to the message-type strings\n# `_create_message_from_message_type` accepts. Read by\n# `_convert_to_message`'s dict branch when unpacking that wire shape.\n# Kept as a hardcoded allowlist of strings rather than a class registry\n# lookup so dispatch never resolves to a class chosen by the caller.\n_LC_CONSTRUCTOR_NAME_TO_TYPE: dict[str, str] = {\n    \"HumanMessage\": \"human\",\n    \"HumanMessageChunk\": \"human\",\n    \"AIMessage\": \"ai\",\n    \"AIMessageChunk\": \"ai\",\n    \"SystemMessage\": \"system\",\n    \"SystemMessageChunk\": \"system\",\n    \"FunctionMessage\": \"function\",","sourceCodeStart":662,"sourceCodeEnd":698,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/messages/utils.py#L662-L698","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","solutions":["Map the role string to a supported one before conversion (see the supported list in the error message)","Check for stray whitespace/case: use lowercase exact strings","For new provider roles like 'developer', upgrade langchain-core to a version that supports them"],"exampleFix":"# before\nconvert_to_messages([{'type': 'tool_message', 'content': '42'}])\n\n# after\nconvert_to_messages([{'type': 'tool', 'content': '42', 'tool_call_id': 'call_1'}])","handlingStrategy":"validation","validationCode":"VALID_ROLES = {'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', 'developer', 'remove'}\n\ndef coerce_role(role: str) -> str:\n    aliases = {'tool_message': 'tool', 'bot': 'ai'}\n    r = role.strip().lower()\n    if r not in VALID_ROLES:\n        r = aliases.get(r, r)\n    assert r in VALID_ROLES, f'unknown role {role!r}'\n    return r","typeGuard":"def is_valid_message_type(t: object) -> bool:\n    return isinstance(t, str) and t.strip().lower() in {\n        'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', 'developer', 'remove'}","tryCatchPattern":"try:\n    msgs = convert_to_messages(raw)\nexcept ValueError as e:\n    if 'MESSAGE_COERCION_FAILURE' in str(e) and 'Unexpected message type' in str(e):\n        raw = [(coerce_role(r), c) for r, c in raw]\n        msgs = convert_to_messages(raw)\n    else:\n        raise","preventionTips":["Map provider role names to langchain roles at ingestion time","Trim/normalize role strings (case, whitespace) before conversion","Upgrade langchain-core when adopting new provider roles like 'developer'"],"tags":["message-coercion","roles","input-validation"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}