langchain-ai/langchain · error · TypeError

Expected either a dictionary with a 'type' key or an object

Error message

Expected either a dictionary with a 'type' key or an object with a 'type' attribute. Instead got type {type(v)}.

What it means

Raised by the internal helper `_get_type` when a value passed to message serialization/conversion is neither a dict containing a 'type' key nor an object with a `type` attribute. The helper is used to derive the message-type discriminator string during (de)serialization of message-like values.

Source

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

    _HAS_LANGCHAIN_TEXT_SPLITTERS = True
except ImportError:
    _HAS_LANGCHAIN_TEXT_SPLITTERS = False

logger = logging.getLogger(__name__)


def _get_type(v: Any) -> str:
    """Get the type associated with the object for serialization purposes."""
    if isinstance(v, dict) and "type" in v:
        result = v["type"]
    elif hasattr(v, "type"):
        result = v.type
    else:
        msg = (
            f"Expected either a dictionary with a 'type' key or an object "
            f"with a 'type' attribute. Instead got type {type(v)}."
        )
        raise TypeError(msg)
    if not isinstance(result, str):
        msg = f"Expected 'type' to be a str, got {type(result).__name__}"
        raise TypeError(msg)
    return result


AnyMessage = Annotated[
    Annotated[AIMessage, Tag(tag="ai")]
    | Annotated[HumanMessage, Tag(tag="human")]
    | Annotated[ChatMessage, Tag(tag="chat")]
    | Annotated[SystemMessage, Tag(tag="system")]
    | Annotated[FunctionMessage, Tag(tag="function")]
    | Annotated[ToolMessage, Tag(tag="tool")]
    | Annotated[AIMessageChunk, Tag(tag="AIMessageChunk")]
    | Annotated[HumanMessageChunk, Tag(tag="HumanMessageChunk")]
    | Annotated[ChatMessageChunk, Tag(tag="ChatMessageChunk")]
    | Annotated[SystemMessageChunk, Tag(tag="SystemMessageChunk")]
    | Annotated[FunctionMessageChunk, Tag(tag="FunctionMessageChunk")]

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Add a 'type' key to the dict ('human', 'ai', 'system', 'tool', ...) or rename 'role' to 'type' where the API expects the langchain shape
  2. Pass proper `BaseMessage` instances (`HumanMessage`, `AIMessage`, ...) instead of raw dicts
  3. If using OpenAI-style dicts, use `convert_to_messages`/`convert_to_openai_messages` helpers instead of the serialization path directly

Example fix

# before
msg = {'role': 'user', 'content': 'hi'}

# after
msg = {'type': 'human', 'content': 'hi'}
# or
from langchain_core.messages import HumanMessage
msg = HumanMessage('hi')
Defensive patterns

Strategy: validation

Validate before calling

def has_type_discriminator(v) -> bool:
    return (isinstance(v, dict) and isinstance(v.get('type'), str)) or (hasattr(v, 'type') and isinstance(getattr(v, 'type'), str))

Type guard

from typing import Any

def is_message_like(v: Any) -> bool:
    if isinstance(v, dict):
        return isinstance(v.get('type'), str)
    return isinstance(getattr(v, 'type', None), str)

Try / catch

try:
    t = _get_type(v)
except TypeError:
    # normalize and retry once with an explicit type
    v = {**v, 'type': 'human'} if isinstance(v, dict) else v
    t = _get_type(v)

Prevention

When it happens

Trigger: Passing a plain dict without a 'type' key (e.g. `{'role': 'user', 'content': 'hi'}`) or an arbitrary object without a `.type` attribute into code paths that call `_get_type`, such as message dict round-tripping utilities.

Common situations: Hand-built message dicts from OpenAI-style payloads that use 'role' instead of 'type'; custom dataclasses passed where a `BaseMessage` or typed dict is expected; partial JSON loaded from a log with the 'type' field stripped.

Related errors


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