{"record":{"id":"60f476f58ad43dc4","repo":"can1357/oh-my-pi","slug":"rpc-message-pagination-returned-an-inconsistent-to","errorCode":null,"errorMessage":"RPC message pagination returned an inconsistent total","messagePattern":"RPC message pagination returned an inconsistent total","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/modes/rpc/rpc-client.ts","lineNumber":883,"sourceCode":"\t\treturn this.#getData<RpcMessagesPage>(response);\n\t}\n\n\t/** Get all messages, draining stable pages when protocol v2 is available. */\n\tasync getMessages(): Promise<AgentMessage[]> {\n\t\tif (this.#protocolVersion === 2) {\n\t\t\ttry {\n\t\t\t\tconst messages: AgentMessage[] = [];\n\t\t\t\tconst seenCursors = new Set<string>();\n\t\t\t\tlet totalMessages: number | undefined;\n\t\t\t\tlet cursor: string | undefined;\n\t\t\t\tdo {\n\t\t\t\t\tconst page = await this.getMessagesPage({ cursor, limit: 256 });\n\t\t\t\t\tif (\n\t\t\t\t\t\t!Number.isSafeInteger(page.totalMessages) ||\n\t\t\t\t\t\tpage.totalMessages < 0 ||\n\t\t\t\t\t\t(totalMessages !== undefined && page.totalMessages !== totalMessages)\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new Error(\"RPC message pagination returned an inconsistent total\");\n\t\t\t\t\ttotalMessages = page.totalMessages;\n\t\t\t\t\tmessages.push(...page.messages);\n\t\t\t\t\tcursor = page.nextCursor;\n\t\t\t\t\tif (cursor && seenCursors.has(cursor)) throw new Error(\"RPC message pagination repeated a cursor\");\n\t\t\t\t\tif (cursor) seenCursors.add(cursor);\n\t\t\t\t} while (cursor);\n\t\t\t\tif (messages.length !== totalMessages)\n\t\t\t\t\tthrow new Error(\"RPC message pagination ended before the advertised total\");\n\t\t\t\treturn messages;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isPageFallbackError(error)) throw error;\n\t\t\t}\n\t\t}\n\t\tconst response = await this.#send({ type: \"get_messages\" });\n\t\treturn this.#getData<{ messages: AgentMessage[] }>(response).messages;\n\t}\n\n\t/**","sourceCodeStart":865,"sourceCodeEnd":901,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/rpc/rpc-client.ts#L865-L901","documentation":"The paginated get-messages loop validates each page's totalMessages: it must be a safe non-negative integer and must not change across pages. A missing, malformed, or shifting total means the server's message set mutated mid-pagination or the page is malformed, so the client aborts rather than return corrupt results (the caller's isPageFallbackError path then falls back to the single get_messages command).","triggerScenarios":"Iterating getMessagesPage({cursor, limit: 256}) where a page returns totalMessages undefined/non-integer/negative, or a different value than the previous page (messages appended/removed while paginating).","commonSituations":"An active session keeps appending messages while history is being fetched; a server bug emitting inconsistent totals; an RPC peer that doesn't fully implement the pagination contract.","solutions":["Retry after the session quiesces (no messages being written).","Check the server/RPC implementation emits a stable, integer totalMessages on every page.","Update client and server to matching versions so the pagination contract holds.","Let the built-in fallback run: the loop catches page-fallback errors and retries with the plain get_messages command."],"exampleFix":"// before\nconst msgs = await client.getMessages(); // fetched while agent streams\n// after\nawait agentIdlePromise; // wait until session stops writing\nconst msgs = await client.getMessages();","handlingStrategy":"retry","validationCode":"function pageLooksValid(page: { totalMessages: unknown }): boolean {\n  return typeof page.totalMessages === \"number\" &&\n    Number.isSafeInteger(page.totalMessages) && page.totalMessages >= 0;\n}","typeGuard":"function isPaginated(page: unknown): page is { messages: unknown[]; totalMessages: number; nextCursor: string | null } {\n  return typeof page === \"object\" && page !== null &&\n    typeof (page as { totalMessages?: unknown }).totalMessages === \"number\";\n}","tryCatchPattern":"try {\n  const msgs = await client.getMessages();\n} catch (err) {\n  if ((err as Error).message.startsWith(\"RPC message pagination\")) {\n    await sessionIdle;\n    const msgs = await client.getMessages(); // client has a get_messages fallback\n  } else throw err;\n}","preventionTips":["Fetch history only while the session is idle.","Keep the built-in get_messages fallback enabled.","Keep client/server versions matched.","Validate totals yourself if using getMessagesPage directly."],"tags":["rpc","pagination","consistency","ipc"],"backgroundTag":"pagination-total-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}