{"record":{"id":"5c368ddcd81b60ee","repo":"can1357/oh-my-pi","slug":"rpc-message-page-limit-must-be-between-1-and-max","errorCode":null,"errorMessage":"RPC message page limit must be between 1 and ${MAX_RPC_MESSAGE_PAGE_LIMIT}","messagePattern":"RPC message page limit must be between 1 and (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/modes/rpc/rpc-messages.ts","lineNumber":102,"sourceCode":"function sameSnapshot(cursor: RpcMessageCursorPayload, snapshot: RpcMessageSnapshot): boolean {\n\treturn (\n\t\tcursor.sessionId === snapshot.sessionId &&\n\t\tcursor.leafId === snapshot.leafId &&\n\t\tcursor.messageCount === snapshot.messageCount\n\t);\n}\n\n/** Page one stable in-memory message snapshot without crossing the v1 frame budget. */\nexport function pageRpcMessages(\n\tmessages: readonly AgentMessage[],\n\tsnapshot: RpcMessageSnapshot,\n\toptions: RpcMessagesPageOptions = {},\n): RpcMessagesPage {\n\tif (snapshot.messageCount !== messages.length)\n\t\tthrow new Error(\"RPC message snapshot does not match current messages\");\n\tconst limit = options.limit ?? DEFAULT_RPC_MESSAGE_PAGE_LIMIT;\n\tif (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_RPC_MESSAGE_PAGE_LIMIT)\n\t\tthrow new Error(`RPC message page limit must be between 1 and ${MAX_RPC_MESSAGE_PAGE_LIMIT}`);\n\tlet offset = 0;\n\tif (options.cursor !== undefined) {\n\t\tconst cursor = decodeCursor(options.cursor);\n\t\tif (!sameSnapshot(cursor, snapshot))\n\t\t\tthrow new RpcMessagesPageError(RPC_MESSAGES_PAGE_STALE_ERROR, \"stale_cursor\");\n\t\toffset = cursor.offset;\n\t}\n\n\tconst page: AgentMessage[] = [];\n\tlet pageBytes = 2;\n\twhile (offset + page.length < messages.length && page.length < limit) {\n\t\tconst message = messages[offset + page.length];\n\t\tconst messageBytes = Buffer.byteLength(JSON.stringify(message), \"utf8\") + (page.length === 0 ? 0 : 1);\n\t\tif (page.length > 0 && pageBytes + messageBytes > MAX_RPC_MESSAGE_PAGE_BYTES) break;\n\t\tpage.push(message);\n\t\tpageBytes += messageBytes;\n\t}\n","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/rpc/rpc-messages.ts#L84-L120","documentation":"pageRpcMessages validates the limit option: it must be a safe integer between 1 and 256 (MAX_RPC_MESSAGE_PAGE_LIMIT). Passing undefined uses the default (100); anything outside the range — 0, negative, non-integers, NaN, or >256 — throws this error.","triggerScenarios":"Calling pageRpcMessages / get_messages_page with limit: 0, limit: -1, limit: 1000, limit: 12.5, or limit: NaN; forwarding an unvalidated user-supplied query parameter as limit.","commonSituations":"Clients implementing 'fetch all' by passing a huge limit; parsing limit from a URL/string without Number() conversion ('20' as string fails Number.isSafeInteger? — actually strings fail the check); tests probing bounds; UI pagination math producing 0.","solutions":["Clamp limit before calling: Math.min(Math.max(1, Math.floor(limit)), 256), or omit it to use the default 100.","Convert string inputs to numbers first and reject NaN: const n = Number(raw); if (!Number.isSafeInteger(n)) omit or default.","Use limit ≤ 256 and iterate pages with nextCursor instead of trying to fetch everything in one request.","Guard UI/state code so limit can never become 0 or negative when computing page sizes."],"exampleFix":"// before\nconst limit = Number(query.get('limit')); // NaN when absent\npage(messages, snapshot, { limit });\n// after\nconst raw = Number(query.get('limit'));\nconst limit = Number.isSafeInteger(raw) ? Math.min(Math.max(1, raw), 256) : undefined;\npage(messages, snapshot, limit ? { limit } : {});","handlingStrategy":"validation","validationCode":"function clampLimit(raw: unknown): number | undefined {\n  const n = typeof raw === \"string\" ? Number(raw) : raw;\n  return typeof n === \"number\" && Number.isSafeInteger(n) && n >= 1 && n <= 256 ? n : undefined;\n}","typeGuard":"function isValidPageLimit(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isSafeInteger(v) && v >= 1 && v <= 256;\n}","tryCatchPattern":"try {\n  return page(messages, snapshot, { limit });\n} catch (err) {\n  if (String(err.message).startsWith(\"RPC message page limit\")) {\n    return page(messages, snapshot, {}); // fall back to default limit 100\n  }\n  throw err;\n}","preventionTips":["Clamp user-supplied limits into [1, 256] before calling","Coerce string query params with Number() and verify Number.isSafeInteger","Page with nextCursor instead of requesting oversized limits"],"tags":["rpc","pagination","validation","input-validation"],"backgroundTag":"invalid-page-limit","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}