{"record":{"id":"8bb7f488a0bc96e6","repo":"can1357/oh-my-pi","slug":"rpc-message-pagination-ended-before-the-advertised","errorCode":null,"errorMessage":"RPC message pagination ended before the advertised total","messagePattern":"RPC message pagination ended before the advertised total","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/modes/rpc/rpc-client.ts","lineNumber":891,"sourceCode":"\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/**\n\t * Get list of OAuth providers available for login, with their current authentication status.\n\t */\n\tasync getLoginProviders(): Promise<Array<{ id: string; name: string; available: boolean; authenticated: boolean }>> {\n\t\tconst response = await this.#send({ type: \"get_login_providers\" });\n\t\treturn this.#getData<{\n\t\t\tproviders: Array<{ id: string; name: string; available: boolean; authenticated: boolean }>;\n\t\t}>(response).providers;\n\t}","sourceCodeStart":873,"sourceCodeEnd":909,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/rpc/rpc-client.ts#L873-L909","documentation":"After the do/while pagination loop finishes (cursor is falsy), the client asserts messages.length === totalMessages. Fewer messages than advertised means pages ended early — dropped pages or a total computed over a different set — so the client throws instead of returning partial history.","triggerScenarios":"The last page returns nextCursor=null but the accumulated messages array is shorter than the advertised totalMessages (server skipped a page, filtered rows silently, or the total counted messages never returned).","commonSituations":"Server-side truncation/eviction of history between total computation and page reads; a buggy or older RPC host implementation; mid-fetch message compaction.","solutions":["Update the RPC server so its final cursor and totals match the returned rows.","Retry when background history eviction/compaction is idle.","Rely on the client's own page-fallback path, which retries with the monolithic get_messages command.","Verify with server logs how many messages each page returned."],"exampleFix":"// before\nconst all = await client.getMessages(); // during compaction\n// after\nawait compactionIdle;\nconst all = await client.getMessages();","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const msgs = await client.getMessages();\n} catch (err) {\n  if ((err as Error).message.includes(\"ended before the advertised total\")) {\n    await backgroundJobsIdle;\n    const msgs = await client.getMessages();\n  } else throw err;\n}","preventionTips":["Pause history eviction/compaction during history fetches.","Upgrade the server so its final page matches the advertised total.","Prefer the client's automatic page-fallback path.","Cross-check page counts in server logs when it reproduces."],"tags":["rpc","pagination","data-integrity","ipc"],"backgroundTag":"pagination-total-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}