{"record":{"id":"5364b2835d434b57","repo":"mastra-ai/mastra","slug":"page-must-be-0-5364b2","errorCode":null,"errorMessage":"page must be >= 0","messagePattern":"page must be >= 0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/domains/memory/inmemory.ts","lineNumber":133,"sourceCode":"    orderBy,\n  }: StorageListMessagesInput): Promise<StorageListMessagesOutput> {\n    const metadataFilter = validateStorageMetadataFilter(filter?.metadata);\n    // Normalize threadId to array\n    const threadIds = Array.isArray(threadId) ? threadId : [threadId];\n\n    if (threadIds.length === 0 || threadIds.some(id => !id.trim())) {\n      throw new Error('threadId must be a non-empty string or array of non-empty strings');\n    }\n\n    const threadIdSet = new Set(threadIds);\n\n    const { field, direction } = this.parseOrderBy(orderBy, 'ASC');\n\n    // Normalize perPage for query (false → MAX_SAFE_INTEGER, 0 → 0, undefined → 40)\n    const perPage = normalizePerPage(perPageInput, 40);\n\n    if (page < 0) {\n      throw new Error('page must be >= 0');\n    }\n\n    // Prevent unreasonably large page values that could cause performance issues\n    const maxOffset = Number.MAX_SAFE_INTEGER / 2;\n    if (page * perPage > maxOffset) {\n      throw new Error('page value too large');\n    }\n\n    // Calculate offset from page\n    const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);\n\n    // When perPage is 0 with no includes, there's nothing to return.\n    if (perPage === 0 && (!include || include.length === 0)) {\n      return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };\n    }\n\n    // Step 1: Get messages matching threadId(s) and optionally resourceId\n    let threadMessages = Array.from(this.db.messages.values()).filter((msg: any) => {","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/domains/memory/inmemory.ts#L115-L151","documentation":"listMessages validates that the page argument is zero or a positive number before computing an offset. Negative pages have no meaning in offset-based pagination, so the in-memory storage domain throws immediately. This is a fail-fast argument validation, not a state error.","triggerScenarios":"Calling listMessages({ threadId, page: -1 }) or any negative page, typically from a decrementing page counter that goes below 0, or from user-supplied query params parsed with parseInt without clamping.","commonSituations":"UI 'previous page' buttons decrementing past the first page; URL query strings like ?page=-1; Math.ceil/floor rounding producing -0-adjacent negatives on empty datasets; hand-rolled cursors.","solutions":["Clamp page before calling: Math.max(0, page).","Validate user/URL-supplied page numbers at the boundary (NaN check plus >= 0) before invoking storage.","If iterating pages in a loop, start at 0 and guard decrement logic against going below zero."],"exampleFix":"// before\nconst page = Number(searchParams.get('page'));\nawait storage.listMessages({ threadId, page });\n// after\nconst page = Math.max(0, Number.parseInt(searchParams.get('page') ?? '0', 10) || 0);\nawait storage.listMessages({ threadId, page });","handlingStrategy":"validation","validationCode":"function normalizePage(raw) {\n  const n = Number(raw);\n  return Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;\n}\nawait storage.listMessages({ threadId, page: normalizePage(rawPage) });","typeGuard":"function isValidPage(v: unknown): v is number {\n  return typeof v === 'number' && Number.isFinite(v) && Number.isInteger(v) && v >= 0;\n}","tryCatchPattern":null,"preventionTips":["Clamp every page value with Math.max(0, page) at the data-access boundary.","Convert 1-based UI page numbers to this API's 0-based page explicitly.","Validate parsed query params (NaN and range checks) before they reach storage."],"tags":["validation","pagination","storage","argument-error"],"backgroundTag":"invalid-pagination-args","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}