{"record":{"id":"f26cadbd12c7e30d","repo":"langchain-ai/langchain","slug":"unknown-basemessage-type-message-class","errorCode":null,"errorMessage":"Unknown BaseMessage type {message.__class__}.","messagePattern":"Unknown BaseMessage type (.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/messages/utils.py","lineNumber":2227,"sourceCode":"def _get_message_openai_role(message: BaseMessage) -> str:\n    if isinstance(message, AIMessage):\n        return \"assistant\"\n    if isinstance(message, HumanMessage):\n        return \"user\"\n    if isinstance(message, ToolMessage):\n        return \"tool\"\n    if isinstance(message, SystemMessage):\n        role = message.additional_kwargs.get(\"__openai_role__\", \"system\")\n        if not isinstance(role, str):\n            msg = f\"Expected '__openai_role__' to be a str, got {type(role).__name__}\"\n            raise TypeError(msg)\n        return role\n    if isinstance(message, FunctionMessage):\n        return \"function\"\n    if isinstance(message, ChatMessage):\n        return message.role\n    msg = f\"Unknown BaseMessage type {message.__class__}.\"\n    raise ValueError(msg)\n\n\ndef _convert_to_openai_tool_calls(tool_calls: list[ToolCall]) -> list[dict[str, Any]]:\n    return [\n        {\n            \"type\": \"function\",\n            \"id\": tool_call[\"id\"],\n            \"function\": {\n                \"name\": tool_call[\"name\"],\n                \"arguments\": json.dumps(tool_call[\"args\"], ensure_ascii=False),\n            },\n        }\n        for tool_call in tool_calls\n    ]\n\n\ndef count_tokens_approximately(\n    messages: Iterable[MessageLikeRepresentation],","sourceCodeStart":2209,"sourceCodeEnd":2245,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/messages/utils.py#L2209-L2245","documentation":"`_get_message_openai_role` maps known message classes to OpenAI roles (AIMessage->assistant, HumanMessage->user, ToolMessage->tool, SystemMessage->system/override, FunctionMessage->function, ChatMessage->its own role). A BaseMessage subclass that is none of these cannot be assigned a role, so a ValueError with the class name is raised. Unlike error 170/171 this is about role assignment during OpenAI-format conversion, not chunk coercion.","triggerScenarios":"Passing a `class CustomMessage(BaseMessage)` instance into code that converts messages to OpenAI request payloads; exotic message types from older LangChain versions or third-party packages that no longer match the isinstance chain.","commonSituations":"Migrating legacy `Chain` code that defined its own message types; combining langchain-core with frameworks whose adapters leak their own BaseMessage subclasses.","solutions":["Subclass ChatMessage and set its `role` field — ChatMessage is explicitly supported for custom roles: `ChatMessage(content=\"hi\", role=\"user\")`.","Or subclass one of the standard classes (HumanMessage/AIMessage/SystemMessage) so a role is inferable.","Or convert custom messages to a standard type before invoking OpenAI-format conversion."],"exampleFix":"// before\nclass CustomMessage(BaseMessage): ...\n\n// after\nfrom langchain_core.messages import ChatMessage\nChatMessage(content=\"hi\", role=\"user\")","handlingStrategy":"type-guard","validationCode":"from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage, FunctionMessage, ChatMessage\n\ndef has_openai_role(m) -> bool:\n    return isinstance(m, (AIMessage, HumanMessage, SystemMessage, ToolMessage, FunctionMessage, ChatMessage))","typeGuard":"def is_role_assignable(m) -> bool:\n    return isinstance(m, (AIMessage, HumanMessage, SystemMessage, ToolMessage, FunctionMessage, ChatMessage))","tryCatchPattern":"try:\n    convert_to_openai_messages([msg])\nexcept ValueError as e:\n    if \"Unknown BaseMessage type\" in str(e):\n        msg = ChatMessage(content=msg.content, role=\"user\")  # choose an explicit role\n        convert_to_openai_messages([msg])","preventionTips":["Use ChatMessage for custom roles instead of subclassing BaseMessage","Adapter-validate third-party message types at ingestion","Keep a mapping (custom type -> standard type) in one place"],"tags":["messages","openai","roles","custom-classes","type-coercion"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}