{"record":{"id":"cc9b3a0a0e1dade7","repo":"can1357/oh-my-pi","slug":"rpc-message-pagination-returned-an-inconsistent-to-cc9b3a","errorCode":null,"errorMessage":"RPC message pagination returned an inconsistent total","messagePattern":"RPC message pagination returned an inconsistent total","errorType":"exception","errorClass":"RpcError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/client.py","lineNumber":1035,"sourceCode":"        return parse_todo_phases(payload.get(\"todoPhases\"))\n\n    def clear_todos(self) -> tuple[TodoPhase, ...]:\n        return self.set_todos(())\n\n    def get_messages(self) -> tuple[AgentMessage, ...]:\n        if self._protocol_version == 2:\n            try:\n                messages: list[AgentMessage] = []\n                seen_cursors: set[str] = set()\n                total_messages: int | None = None\n                cursor: str | None = None\n                while True:\n                    page = self.get_messages_page(cursor=cursor, limit=256)\n                    if (\n                        total_messages is not None\n                        and page.total_messages != total_messages\n                    ):\n                        raise RpcError(\n                            \"RPC message pagination returned an inconsistent total\"\n                        )\n                    total_messages = page.total_messages\n                    messages.extend(page.messages)\n                    cursor = page.next_cursor\n                    if cursor is None:\n                        break\n                    if cursor in seen_cursors:\n                        raise RpcError(\"RPC message pagination repeated a cursor\")\n                    seen_cursors.add(cursor)\n                if len(messages) != total_messages:\n                    raise RpcError(\n                        \"RPC message pagination ended before the advertised total\"\n                    )\n                return tuple(messages)\n            except RpcCommandError as error:\n                if error.command != \"get_messages_page\" or not (\n                    error.code in _RPC_MESSAGES_PAGE_FALLBACK_CODES","sourceCodeStart":1017,"sourceCodeEnd":1053,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/client.py#L1017-L1053","documentation":"When collecting all messages, the client pages through get_messages_page() and remembers the first page's total_messages. If a later page reports a different total, the client raises this RpcError because the pages are no longer from one consistent snapshot — appending them would yield a corrupt transcript.","triggerScenarios":"Calling the client's get-all-messages path (loop around get_messages_page(cursor, limit=256)) while the server-side session is concurrently appending, trimming, or compacting messages so page.total_messages changes between requests.","commonSituations":"Reading a live agent session's history while it is actively generating messages; server-side compaction/pruning running mid-pagination; resuming a session in another client that adds messages between page fetches.","solutions":["Re-run the full pagination from cursor=None once the session is idle/paused","Read the transcript from a finished session, or snapshot it server-side before paging","Upgrade server/client so pagination is served from a stable snapshot","Catch RpcError here and retry the whole pagination loop on transient inconsistency"],"exampleFix":"# before: paginating a live, still-growing session\nmessages = client.get_all_messages()  # total changed mid-loop -> RpcError\n\n# after: retry after the run settles\nfor _ in range(3):\n    try:\n        messages = client.get_all_messages()\n        break\n    except RpcError:\n        time.sleep(1)  # session still mutating; retry from scratch","handlingStrategy":"retry","validationCode":"# page only when the session is not actively producing messages\nstate = client.get_state()\nif getattr(state, \"active\", False):\n    raise RuntimeError(\"session busy; wait for the run to finish before paginating\")\n","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        messages = client.get_all_messages()\n        break\n    except RpcError as e:\n        if \"inconsistent total\" in str(e) and attempt < 2:\n            time.sleep(1)\n            continue\n        raise\n","preventionTips":["Paginate only after the agent run reaches an idle/finished state","Retry the entire pagination loop on inconsistency (pages are re-read from scratch)","Avoid concurrent clients appending to the same session while paginating","Prefer a server version that snapshots totals per pagination run"],"tags":["rpc","pagination","consistency","race-condition"],"backgroundTag":"pagination-inconsistent-total","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}