langchain-ai/langchain · error · ValueError
Unknown BaseMessage type {message.__class__}.
Error message
Unknown BaseMessage type {message.__class__}. What it means
`_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.
Source
Thrown at libs/core/langchain_core/messages/utils.py:2227
def _get_message_openai_role(message: BaseMessage) -> str:
if isinstance(message, AIMessage):
return "assistant"
if isinstance(message, HumanMessage):
return "user"
if isinstance(message, ToolMessage):
return "tool"
if isinstance(message, SystemMessage):
role = message.additional_kwargs.get("__openai_role__", "system")
if not isinstance(role, str):
msg = f"Expected '__openai_role__' to be a str, got {type(role).__name__}"
raise TypeError(msg)
return role
if isinstance(message, FunctionMessage):
return "function"
if isinstance(message, ChatMessage):
return message.role
msg = f"Unknown BaseMessage type {message.__class__}."
raise ValueError(msg)
def _convert_to_openai_tool_calls(tool_calls: list[ToolCall]) -> list[dict[str, Any]]:
return [
{
"type": "function",
"id": tool_call["id"],
"function": {
"name": tool_call["name"],
"arguments": json.dumps(tool_call["args"], ensure_ascii=False),
},
}
for tool_call in tool_calls
]
def count_tokens_approximately(
messages: Iterable[MessageLikeRepresentation],View on GitHub (pinned to e32fa9a52e)
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.
Example fix
// before class CustomMessage(BaseMessage): ... // after from langchain_core.messages import ChatMessage ChatMessage(content="hi", role="user")
Defensive patterns
Strategy: type-guard
Validate before calling
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage, FunctionMessage, ChatMessage
def has_openai_role(m) -> bool:
return isinstance(m, (AIMessage, HumanMessage, SystemMessage, ToolMessage, FunctionMessage, ChatMessage)) Type guard
def is_role_assignable(m) -> bool:
return isinstance(m, (AIMessage, HumanMessage, SystemMessage, ToolMessage, FunctionMessage, ChatMessage)) Try / catch
try:
convert_to_openai_messages([msg])
except ValueError as e:
if "Unknown BaseMessage type" in str(e):
msg = ChatMessage(content=msg.content, role="user") # choose an explicit role
convert_to_openai_messages([msg]) Prevention
- 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
When it happens
Trigger: 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.
Common situations: Migrating legacy `Chain` code that defined its own message types; combining langchain-core with frameworks whose adapters leak their own BaseMessage subclasses.
Related errors
- Unrecognized content block at messages[{i}].content[{j}] has
- OpenAI messages can only support text and image data. Receiv
- Expected '__openai_role__' to be a str, got {type(role).__na
- Invalid input type {type(model_input)}. Must be a PromptValu
- Expected invoke to return an AIMessage, but got {type(messag
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/f26cadbd12c7e30d.
Report an issue: GitHub.