{"record":{"id":"8f65fb53afecb402","repo":"can1357/oh-my-pi","slug":"rpc-message-pagination-repeated-a-cursor","errorCode":null,"errorMessage":"RPC message pagination repeated a cursor","messagePattern":"RPC message pagination repeated a cursor","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/modes/rpc/rpc-client.ts","lineNumber":887,"sourceCode":"\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/**\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\" });","sourceCodeStart":869,"sourceCodeEnd":905,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/rpc/rpc-client.ts#L869-L905","documentation":"While walking message pages, the client records every cursor it has seen; if the server ever returns a nextCursor it already returned, the loop would be infinite, so it throws. This is server-side pagination loop detection.","triggerScenarios":"getMessagesPage returns a nextCursor equal to one previously returned — a buggy cursor implementation or a cursor that does not advance past already-fetched pages.","commonSituations":"An RPC server that regenerates the same cursor for the tail page; deleting messages mid-pagination so the cursor window doesn't advance; a non-conforming custom RPC host.","solutions":["Upgrade/fix the RPC server so nextCursor strictly advances (or is null when done).","Retry pagination once message deletion/insertion has settled.","Avoid mutating (deleting) messages during a history fetch.","Use the non-paginated get_messages fallback path."],"exampleFix":"// before\nconst msgs = await client.getMessages(); // while jobs prune old messages\n// after\nawait pruningComplete;\nconst msgs = await client.getMessages();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const msgs = await client.getMessages();\n} catch (err) {\n  if ((err as Error).message.includes(\"repeated a cursor\")) {\n    logger.error(\"server pagination loop — upgrade the RPC server\");\n  } else throw err;\n}","preventionTips":["Don't delete messages while paginating history.","Use a conforming server whose nextCursor strictly advances.","Report cursor loops as server bugs.","Bound your own wait time so a loop cannot hang the app."],"tags":["rpc","pagination","cursor","infinite-loop"],"backgroundTag":"pagination-cursor-loop","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}