can1357/oh-my-pi · error · RpcError

get_messages_page response has an invalid totalMessages

Error message

get_messages_page response has an invalid totalMessages

What it means

_parse_messages_page validates the raw get_messages_page payload before constructing MessagesPage. totalMessages must be a true non-negative int (bools excluded since bool subclasses int in Python); otherwise the client raises this RpcError. It protects callers from servers returning malformed or schema-drifted pagination responses.

Source

Thrown at python/omp-rpc/src/omp_rpc/client.py:1074

                        _RPC_MESSAGES_PAGE_BUSY_ERROR,
                        _RPC_MESSAGES_PAGE_STALE_ERROR,
                    )
                ):
                    raise
        payload = self._request("get_messages")
        return parse_agent_messages(cast(JsonValue | None, payload.get("messages")))

    def get_messages_page(
        self, *, cursor: str | None = None, limit: int | None = None
    ) -> MessagesPage:
        payload = self._request("get_messages_page", cursor=cursor, limit=limit)
        raw_total = payload.get("totalMessages")
        if (
            not isinstance(raw_total, int)
            or isinstance(raw_total, bool)
            or raw_total < 0
        ):
            raise RpcError("get_messages_page response has an invalid totalMessages")
        raw_cursor = payload.get("nextCursor")
        if raw_cursor is not None and not isinstance(raw_cursor, str):
            raise RpcError("get_messages_page response has an invalid nextCursor")
        return MessagesPage(
            messages=parse_agent_messages(
                cast(JsonValue | None, payload.get("messages"))
            ),
            total_messages=raw_total,
            next_cursor=raw_cursor,
        )

    def set_custom_tools(self, tools: Sequence[HostTool[Any, Any]]) -> tuple[str, ...]:
        self._custom_tools = tuple(tools)
        if self._process is None:
            return tuple(tool.name for tool in self._custom_tools)

        payload = self._request(
            "set_host_tools",

View on GitHub (pinned to 9690622007)

Solutions

  1. Upgrade client and server to matching versions so the response schema aligns
  2. Fix the mock/stub server to return totalMessages as a non-negative integer
  3. Log the raw payload to identify the actual field name/type the server sends
  4. If a proxy rewrites responses, bypass or correct it

Example fix

# before: stub returns a string total
return {"messages": [], "totalMessages": "3", "nextCursor": None}

# after
return {"messages": [], "totalMessages": 3, "nextCursor": None}
Defensive patterns

Strategy: type-guard

Validate before calling

def valid_total(payload: dict) -> bool:
    t = payload.get("totalMessages")
    return isinstance(t, int) and not isinstance(t, bool) and t >= 0

Type guard

def is_valid_total(v: object) -> bool:
    return isinstance(v, int) and not isinstance(v, bool) and v >= 0

Try / catch

try:
    page = client.get_messages_page(cursor=None, limit=256)
except RpcError as e:
    if "invalid totalMessages" in str(e):
        page = None  # schema mismatch: upgrade server or fix the stub
    else:
        raise

Prevention

When it happens

Trigger: Server responds to get_messages_page with totalMessages missing, null, a string, a float, a bool, or a negative number — typically a server/client version skew or a stub server returning an unexpected shape.

Common situations: Custom or mocked RPC servers with sloppy payloads; older server builds whose field was named differently (e.g. 'total'); proxies that re-serialize numbers as strings.

Related errors


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