{"record":{"id":"ed019bf66d5f5762","repo":"mastra-ai/mastra","slug":"perpage-must-be-a-positive-integer","errorCode":null,"errorMessage":"perPage must be a positive integer","messagePattern":"perPage must be a positive integer","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"packages/server/src/server/handlers/workflows.ts","lineNumber":389,"sourceCode":"      }\n\n      // Support both page/perPage and limit/offset for backwards compatibility\n      // If page/perPage provided, use directly; otherwise convert from limit/offset\n      let finalPage = page;\n      let finalPerPage = perPage;\n\n      if (finalPerPage === undefined && limit !== undefined) {\n        finalPerPage = limit;\n      }\n      if (finalPage === undefined && offset !== undefined && finalPerPage !== undefined && finalPerPage > 0) {\n        finalPage = Math.floor(offset / finalPerPage);\n      }\n\n      if (\n        finalPerPage !== undefined &&\n        (typeof finalPerPage !== 'number' || !Number.isInteger(finalPerPage) || finalPerPage <= 0)\n      ) {\n        throw new HTTPException(400, { message: 'perPage must be a positive integer' });\n      }\n      if (finalPage !== undefined && (!Number.isInteger(finalPage) || finalPage < 0)) {\n        throw new HTTPException(400, { message: 'page must be a non-negative integer' });\n      }\n      const { workflow } = await listWorkflowsFromSystem({ mastra, workflowId });\n      if (!workflow) {\n        throw new HTTPException(404, { message: 'Workflow not found' });\n      }\n      const workflowRuns = (await workflow.listWorkflowRuns({\n        fromDate: fromDate ? (typeof fromDate === 'string' ? new Date(fromDate) : fromDate) : undefined,\n        toDate: toDate ? (typeof toDate === 'string' ? new Date(toDate) : toDate) : undefined,\n        perPage: finalPerPage,\n        page: finalPage,\n        resourceId: effectiveResourceId,\n        status,\n      })) || {\n        runs: [],\n        total: 0,","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/workflows.ts#L371-L407","documentation":"The workflow-runs route validates pagination inputs and throws 400 'perPage must be a positive integer' when `perPage` (or its limit/offset back-compat equivalent) is present but not a positive integer — e.g. 0, a negative number, a float, or a string. The guard exists because perPage is forwarded directly to storage-level listWorkflowRuns which expects a valid positive integer page size.","triggerScenarios":"Calling GET /api/workflows/:id/runs?perPage=0 or perPage=-5 or perPage=2.5; sending perPage as a string '10' from a raw query param without parsing; converting legacy limit=0 to perPage=0.","commonSituations":"Reading query params straight from URL search params (always strings) and passing them through unvalidated; UI sliders allowing 0 page size; a config defaulting page size to 0 meaning 'unlimited'.","solutions":["Send perPage as a positive integer (>= 1)","Parse and validate query strings with Number.parseInt before sending","Omit perPage entirely to use the server default instead of sending 0","Map legacy limit values, clamping 0/negatives to a valid default"],"exampleFix":"// before\nconst perPage = searchParams.get('perPage'); // '20' (string)\nawait fetch(`/api/workflows/${id}/runs?perPage=${perPage}`);\n// after\nconst perPage = Number.parseInt(searchParams.get('perPage') ?? '', 10);\nif (Number.isInteger(perPage) && perPage > 0) {\n  await fetch(`/api/workflows/${id}/runs?perPage=${perPage}`);\n}","handlingStrategy":"validation","validationCode":"function sanitizePerPage(input) {\n  const n = typeof input === 'string' ? Number.parseInt(input, 10) : input;\n  if (!Number.isInteger(n) || n <= 0) return undefined; // let server use default\n  return n;\n}","typeGuard":"function isPositiveInteger(v: unknown): v is number {\n  return typeof v === 'number' && Number.isInteger(v) && v > 0;\n}","tryCatchPattern":"try {\n  return await workflow.listWorkflowRuns({ perPage });\n} catch (e) {\n  if (e?.status === 400 && /perPage must be a positive integer/i.test(e?.message ?? '')) {\n    return await workflow.listWorkflowRuns({ perPage: 10 }); // safe default\n  }\n  throw e;\n}","preventionTips":["Always parse query-string params with Number.parseInt — they arrive as strings","Clamp UI page-size selectors to a minimum of 1","Omit perPage instead of sending 0 when 'all/unlimited' is intended","Share one sanitize helper for all paginated requests"],"tags":["http-400","workflows","pagination","validation","server-api"],"backgroundTag":"invalid-pagination-parameter","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}