can1357/oh-my-pi · error · ValueError

{field}.role must be 'assistant'

Error message

{field}.role must be 'assistant'

What it means

The omp-rpc protocol parser validates every agent message's role before casting. This error is raised when a payload passed to _parse_assistant_message (via parse_assistant_message_event, e.g. the 'partial' of an assistantMessageEvent or a 'done' event's message) parses as a valid agent message but its role is not 'assistant'. The library throws it because the typed AssistantMessage contract requires role == 'assistant'; a different role would silently violate the type cast that follows.

Source

Thrown at python/omp-rpc/src/omp_rpc/protocol.py:300

    result: list[str] = []
    for item in values:
        if not isinstance(item, str):
            raise ValueError(f"{field} must contain only strings")
        result.append(item)
    return tuple(result) or None


def _parse_agent_message(payload: JsonObject, *, field: str) -> AgentMessage:
    _require_literal(
        payload.get("role"), _AGENT_MESSAGE_ROLE_VALUES, field=f"{field}.role"
    )
    return cast(AgentMessage, _clone_json_object(payload, field=field))


def _parse_assistant_message(payload: JsonObject, *, field: str) -> AssistantMessage:
    message = _parse_agent_message(payload, field=field)
    if message.get("role") != "assistant":
        raise ValueError(f"{field}.role must be 'assistant'")
    return cast(AssistantMessage, message)


def _parse_tool_result_message(payload: JsonObject, *, field: str) -> ToolResultMessage:
    message = _parse_agent_message(payload, field=field)
    if message.get("role") != "toolResult":
        raise ValueError(f"{field}.role must be 'toolResult'")
    return cast(ToolResultMessage, message)


def parse_agent_messages(payload: JsonValue | None) -> tuple[AgentMessage, ...]:
    if payload is None:
        return ()
    if not isinstance(payload, list):
        raise ValueError("messages must be a list")

    messages: list[AgentMessage] = []
    for index, item in enumerate(payload):

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect the notification payload and ensure the partial/message object inside the assistantMessageEvent has role='assistant'
  2. Fix the server/emitter side so assistant events carry assistant-role messages
  3. If consuming raw JSON, route messages with other roles through the appropriate parser (parse_agent_messages) instead of the assistant-specific one
  4. Check for protocol version mismatch between client and server and upgrade the matching omp-rpc package

Example fix

// before
parse_assistant_message_event({"type": "start", "partial": {"role": "user", ...}})
// after
parse_assistant_message_event({"type": "start", "partial": {"role": "assistant", "content": [], ...}})
Defensive patterns

Strategy: type-guard

Validate before calling

def is_assistant_message(m: dict) -> bool:
    return isinstance(m, dict) and m.get("role") == "assistant"

partial = event.get("partial")
if not is_assistant_message(partial):
    raise ValueError("event partial is not an assistant message")

Type guard

from typing import TypeGuard

def is_assistant_message(m: object) -> TypeGuard[dict]:
    return isinstance(m, dict) and m.get("role") == "assistant"

Try / catch

try:
    event = parse_assistant_message_event(payload)
except ValueError as e:
    logger.warning("malformed assistant event: %s", e)
    event = None

Prevention

When it happens

Trigger: Passing a notification whose assistantMessageEvent.partial (or assistantMessageEvent.message for done events) has role 'user' or 'toolResult' instead of 'assistant'; a server bug serializing the wrong message into an assistant event; mixing up message objects when hand-constructing events.

Common situations: A custom or older server implementation emits the full transcript message rather than the assistant message in event partials; client code plumbs user messages into assistant-message-event handlers; a protocol version change renamed or reshaped roles.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/40a5fe7106919600. Report an issue: GitHub.