langchain-ai/deepagents · error · TypeError

LangGraph returned a non-list messages channel.

Error message

LangGraph returned a non-list messages channel.

What it means

The hydrated thread state's `messages` channel must be a list; the SDK converts each entry to a message object via convert_to_messages. If LangGraph reports a non-list messages channel, the state shape is invalid and _hydrate_state raises this TypeError instead of iterating over something that cannot be converted. Like the non-object state error, it points to a server/runtime or state-corruption problem.

Source

Thrown at libs/code/deepagents_code/offload_api.py:557

    """Hydrate serialized checkpoint messages for the compaction service.

    Args:
        values: State values returned by LangGraph Server.

    Returns:
        A shallow state copy containing LangChain message objects.

    Raises:
        TypeError: If the server returns an unexpected state shape.
    """
    if not isinstance(values, dict):
        msg = "LangGraph returned non-object thread state."
        raise TypeError(msg)
    state = dict(values)
    messages = state.get("messages", [])
    if not isinstance(messages, list):
        msg = "LangGraph returned a non-list messages channel."
        raise TypeError(msg)
    state["messages"] = convert_to_messages(messages)

    # LangGraph serializes the summary stored inside the private event channel
    # independently of the top-level `messages` channel. The summarization SDK
    # prepends it to the effective conversation, so it must be a message object
    # too rather than the serialized dict returned by the thread API.
    event = state.get("_summarization_event")
    if isinstance(event, Mapping) and "summary_message" in event:
        hydrated_event = dict(event)
        summary_message = hydrated_event["summary_message"]
        hydrated_event["summary_message"] = convert_to_messages([summary_message])[0]
        state["_summarization_event"] = hydrated_event
    return cast("_OffloadState", state)


def _checkpoint_model_context(
    context: dict[str, Any], state: Mapping[str, object]
) -> dict[str, Any]:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Check the thread's checkpoint/state integrity; recover from an earlier checkpoint or start a new thread if messages is corrupted.
  2. Ensure the deployed graph uses the standard add_messages/messages list channel the SDK expects; align graph and SDK versions.
  3. Log the raw get_state values to see the actual channel type; fix or migrate the stored state.
  4. In tests, make fake get_state return {'messages': [], ...} as a dict of channels.

Example fix

// before
values = await client.threads.get_state(thread_id)  # returns {'messages': {'0': {...}}}
// after
# repair/migrate the channel before offloading
if not isinstance(values.get("messages"), list):
    values["messages"] = list(values["messages"].values())
state = await offload(thread_id, payload)
Defensive patterns

Strategy: try-catch

Validate before calling

state = await client.threads.get_state(thread_id)
messages = state.get("messages", []) if isinstance(state, dict) else []
if not isinstance(messages, list):
    raise RuntimeError(f"Thread {thread_id} has a corrupt 'messages' channel")

Type guard

def has_message_list(state) -> bool:
    return isinstance(state, dict) and isinstance(state.get("messages", []), list)

Try / catch

try:
    state = await offload(thread_id, payload)
except TypeError as exc:
    if "non-list messages channel" in str(exc):
        raise RuntimeError(
            f"Thread {thread_id} 'messages' channel is corrupt; recover from an earlier checkpoint"
        ) from exc
    raise

Prevention

When it happens

Trigger: _execute_offload hydrating state where state.get('messages') came back as a dict, string, or None (key present with wrong type) instead of a list.

Common situations: A thread server storing messages in a legacy/custom channel format, a manually edited or corrupted checkpoint where messages was overwritten, an incompatible graph definition whose 'messages' channel is not a list-typed channel, or a mock returning the wrong shape in tests.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/e9223a0f05942795. Report an issue: GitHub.