{"record":{"id":"2486fc25e7658526","repo":"mastra-ai/mastra","slug":"perpage-must-be-0","errorCode":null,"errorMessage":"perPage must be >= 0","messagePattern":"perPage must be >= 0","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/base.ts","lineNumber":135,"sourceCode":"void _domainKeysExhaustive;\n\n/**\n * Normalizes perPage input for pagination queries.\n *\n * @param perPageInput - The raw perPage value from the user\n * @param defaultValue - The default perPage value to use when undefined (typically 40 for messages, 100 for threads)\n * @returns A numeric perPage value suitable for queries (false becomes MAX_SAFE_INTEGER)\n * @throws Error if perPage is a negative number\n */\nexport function normalizePerPage(perPageInput: number | false | undefined, defaultValue: number): number {\n  if (perPageInput === false) {\n    return Number.MAX_SAFE_INTEGER; // Get all results\n  } else if (perPageInput === 0) {\n    return 0; // Return zero results\n  } else if (typeof perPageInput === 'number' && perPageInput > 0) {\n    return perPageInput; // Valid positive number\n  } else if (typeof perPageInput === 'number' && perPageInput < 0) {\n    throw new Error('perPage must be >= 0');\n  }\n  // For undefined, use default\n  return defaultValue;\n}\n\n/**\n * Calculates pagination offset and prepares perPage value for response.\n * When perPage is false (fetch all), offset is always 0 regardless of page.\n *\n * @param page - The page number (0-indexed)\n * @param perPageInput - The original perPage input (number, false for all, or undefined)\n * @param normalizedPerPage - The normalized perPage value (from normalizePerPage)\n * @returns Object with offset for query and perPage for response\n */\nexport function calculatePagination(\n  page: number,\n  perPageInput: number | false | undefined,\n  normalizedPerPage: number,","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/base.ts#L117-L153","documentation":"Storage's normalizePerPage() sanitizes pagination input before queries. Negative perPage values have no meaning, so the function throws 'perPage must be >= 0'. Zero is allowed (returns zero results) and undefined falls back to a default; only strictly negative numbers are rejected.","triggerScenarios":"Calling any storage listing/query API (threads, messages, traces, workflows, etc.) whose options include perPage (via helpers like perPage, normalizedPerPage, perPageForQuery) with a negative number, e.g. { perPage: -1 } or perPage computed as items.length - offset when offset > items.length.","commonSituations":"Computing perPage as a difference (remaining = total - offset) that goes negative on the last page; passing user-supplied pagination params straight through without clamping; frontend pagination bug where pageSize becomes negative after a filter change.","solutions":["Clamp the value before the call: perPage: Math.max(0, desiredPerPage).","If 'all results' was intended, omit perPage or pass undefined (library applies its default / MAX_SAFE_INTEGER path) rather than a huge/negative number.","Guard computed values — if a subtraction can go negative, fall back to 0 or undefined.","Sanitize external/user input with a coercion helper before passing to storage APIs."],"exampleFix":"// before\nconst remaining = total - offset;\nawait storage.listThreads({ perPage: remaining }); // negative on last page\n// after\nconst remaining = Math.max(0, total - offset);\nawait storage.listThreads({ perPage: remaining });","handlingStrategy":"validation","validationCode":"function safePerPage(value, fallback) {\n  if (value === undefined || value === null) return fallback;\n  const n = Number(value);\n  if (!Number.isFinite(n) || n < 0) return fallback;\n  return Math.floor(n);\n}\n// use: await storage.listThreads({ perPage: safePerPage(input.perPage, 10) })","typeGuard":"function isValidPerPage(v) {\n  return typeof v === 'number' && Number.isFinite(v) && v >= 0;\n}","tryCatchPattern":"try {\n  return await storage.listThreads({ ...opts, perPage: opts.perPage });\n} catch (e) {\n  if (String(e?.message) === 'perPage must be >= 0') {\n    return storage.listThreads({ ...opts, perPage: undefined }); // fall back to default\n  }\n  throw e;\n}","preventionTips":["Clamp computed perPage values with Math.max(0, value) — especially total-minus-offset math.","Sanitize user-supplied pagination before passing it to storage APIs.","Omit perPage entirely when you want the library default or 'all results'.","Add a shared pagination helper for the whole app so perPage is normalized in one place."],"tags":["storage","pagination","validation"],"backgroundTag":"invalid-pagination-parameter","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}