{"record":{"id":"951d122906888c23","repo":"mastra-ai/mastra","slug":"page-must-be-0-951d12","errorCode":null,"errorMessage":"page must be >= 0","messagePattern":"page must be >= 0","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/domains/mcp-clients/inmemory.ts","lineNumber":126,"sourceCode":"    return this.deepCopyConfig(updatedConfig);\n  }\n\n  async delete(id: string): Promise<void> {\n    // Idempotent delete\n    this.db.mcpClients.delete(id);\n    // Also delete all versions for this client\n    await this.deleteVersionsByParentId(id);\n  }\n\n  async list(args?: StorageListMCPClientsInput): Promise<StorageListMCPClientsOutput> {\n    const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status } = args || {};\n    const { field, direction } = this.parseOrderBy(orderBy);\n\n    // Normalize perPage for query (false → MAX_SAFE_INTEGER, 0 → 0, undefined → 100)\n    const perPage = normalizePerPage(perPageInput, 100);\n\n    if (page < 0) {\n      throw new Error('page must be >= 0');\n    }\n\n    // Prevent unreasonably large page values\n    const maxOffset = Number.MAX_SAFE_INTEGER / 2;\n    if (page * perPage > maxOffset) {\n      throw new Error('page value too large');\n    }\n\n    // Get all MCP clients and apply filters\n    let configs = Array.from(this.db.mcpClients.values());\n\n    // Filter by status\n    if (status) {\n      configs = configs.filter(config => config.status === status);\n    }\n\n    // Filter by authorId if provided\n    if (authorId !== undefined) {","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/domains/mcp-clients/inmemory.ts#L108-L144","documentation":"Input validation in `list` of the in-memory MCP clients domain: pagination pages are zero-indexed, so a negative `page` argument is rejected before any query runs. It prevents computing a negative offset.","triggerScenarios":"Calling `list({ page: -1 })` (or any negative value), often from a computed page number like `currentPage - 1` when already on page 0, or from deserialized request params.","commonSituations":"Off-by-one in 'previous page' UI logic; user-supplied query params parsed without clamping; migration from 1-indexed pagination APIs.","solutions":["Clamp page to 0 before calling: `page = Math.max(0, page)`.","Fix previous-page logic to not go below the first page.","Validate/normalize query-string pagination inputs at the API boundary."],"exampleFix":"// before\nconst res = await mcpClients.list({ page: currentPage - 1 });\n// after\nconst res = await mcpClients.list({ page: Math.max(0, currentPage - 1) });","handlingStrategy":"validation","validationCode":"function assertValidPage(page: number): void {\n  if (!Number.isInteger(page) || page < 0) throw new RangeError('page must be an integer >= 0');\n}\nassertValidPage(page);","typeGuard":"const isValidPage = (page: unknown): page is number =>\n  typeof page === 'number' && Number.isInteger(page) && page >= 0;","tryCatchPattern":"try {\n  return await mcpClients.list({ page, perPage });\n} catch (err) {\n  if (err instanceof Error && err.message === 'page must be >= 0') {\n    return mcpClients.list({ page: 0, perPage });\n  }\n  throw err;\n}","preventionTips":["Clamp page with Math.max(0, page) at every call site.","Remember pagination is zero-indexed in this storage layer.","Validate query params with a schema (zod) before passing them in."],"tags":["pagination","validation","argument-error","in-memory"],"backgroundTag":"invalid-pagination-arguments","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}