{"record":{"id":"5fab7dd0b76157f4","repo":"mastra-ai/mastra","slug":"perpage-must-be-0-5fab7d","errorCode":null,"errorMessage":"perPage must be >= 0","messagePattern":"perPage must be >= 0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/domains/memory/base.ts","lineNumber":459,"sourceCode":"        throw new Error(`Metadata key \"${key}\" exceeds maximum length of ${MAX_METADATA_KEY_LENGTH} characters.`);\n      }\n    }\n  }\n\n  /**\n   * Validates pagination parameters and returns safe offset.\n   * @param page - Page number (0-indexed)\n   * @param perPage - Items per page (0 is allowed and returns empty results)\n   * @throws Error if page is negative, perPage is negative/invalid, or offset would overflow\n   */\n  protected validatePagination(page: number, perPage: number): void {\n    if (!Number.isFinite(page) || !Number.isSafeInteger(page) || page < 0) {\n      throw new Error('page must be >= 0');\n    }\n\n    // perPage: 0 is allowed (returns empty results), negative values are rejected\n    if (!Number.isFinite(perPage) || !Number.isSafeInteger(perPage) || perPage < 0) {\n      throw new Error('perPage must be >= 0');\n    }\n\n    // Skip overflow check when perPage is 0 (no offset needed)\n    if (perPage === 0) {\n      return;\n    }\n\n    // Prevent overflow when calculating offset\n    const offset = page * perPage;\n    if (!Number.isSafeInteger(offset) || offset > Number.MAX_SAFE_INTEGER) {\n      throw new Error('page value too large');\n    }\n  }\n\n  /**\n   * Validates pagination input before normalization.\n   * Use this when accepting raw perPageInput (number | false) from callers.\n   *","sourceCodeStart":441,"sourceCodeEnd":477,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/domains/memory/base.ts#L441-L477","documentation":"validatePagination rejects a perPage argument that is negative, non-finite, or not a safe integer. perPage of 0 is explicitly allowed and returns empty results; any negative value throws. This guards the offset computation page * perPage.","triggerScenarios":"Calling listThreads/listMessages with { perPage: -10 }, { perPage: NaN }, or { perPage: 2.5 }; computing perPage from a difference (pageSize - skipped) that went negative.","commonSituations":"Config values read from env as negative numbers; 'show all' logic accidentally producing -1; division/subtraction bugs when deriving page size from remaining items.","solutions":["Clamp perPage: Math.max(0, Math.floor(Number(perPage))).","Use perPage: false to fetch all records instead of a sentinel negative value.","Validate with Number.isSafeInteger(perPage) && perPage >= 0 before calling.","Fix the pageSize derivation so it cannot go negative."],"exampleFix":"// before\nawait storage.listThreads({ page: 0, perPage: total - offset }); // can be negative\n// after\nconst perPage = Math.max(0, total - offset);\nawait storage.listThreads({ page: 0, perPage });","handlingStrategy":"validation","validationCode":"function safePerPage(raw) {\n  const n = Number(raw);\n  return Number.isSafeInteger(n) && n >= 0 ? n : 10;\n}","typeGuard":"function isValidPerPage(p: unknown): p is number {\n  return typeof p === 'number' && Number.isSafeInteger(p) && p >= 0;\n}","tryCatchPattern":"try {\n  await storage.listThreads({ page, perPage: safePerPage(rawPerPage) });\n} catch (e) {\n  if (e instanceof Error && e.message === 'perPage must be >= 0') {\n    return storage.listThreads({ page, perPage: 10 });\n  }\n  throw e;\n}","preventionTips":["Use perPage: false for fetch-all instead of negative sentinels","Clamp derived page sizes with Math.max(0, x)","Validate env-configured page sizes at startup"],"tags":["validation","pagination","storage"],"backgroundTag":"invalid-pagination-parameters","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}