mastra-ai/mastra · error · HTTPException
perPage must be a positive integer
Error message
perPage must be a positive integer
What it means
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.
Source
Thrown at packages/server/src/server/handlers/workflows.ts:389
}
// Support both page/perPage and limit/offset for backwards compatibility
// If page/perPage provided, use directly; otherwise convert from limit/offset
let finalPage = page;
let finalPerPage = perPage;
if (finalPerPage === undefined && limit !== undefined) {
finalPerPage = limit;
}
if (finalPage === undefined && offset !== undefined && finalPerPage !== undefined && finalPerPage > 0) {
finalPage = Math.floor(offset / finalPerPage);
}
if (
finalPerPage !== undefined &&
(typeof finalPerPage !== 'number' || !Number.isInteger(finalPerPage) || finalPerPage <= 0)
) {
throw new HTTPException(400, { message: 'perPage must be a positive integer' });
}
if (finalPage !== undefined && (!Number.isInteger(finalPage) || finalPage < 0)) {
throw new HTTPException(400, { message: 'page must be a non-negative integer' });
}
const { workflow } = await listWorkflowsFromSystem({ mastra, workflowId });
if (!workflow) {
throw new HTTPException(404, { message: 'Workflow not found' });
}
const workflowRuns = (await workflow.listWorkflowRuns({
fromDate: fromDate ? (typeof fromDate === 'string' ? new Date(fromDate) : fromDate) : undefined,
toDate: toDate ? (typeof toDate === 'string' ? new Date(toDate) : toDate) : undefined,
perPage: finalPerPage,
page: finalPage,
resourceId: effectiveResourceId,
status,
})) || {
runs: [],
total: 0,View on GitHub (pinned to 75dd419e61)
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
Example fix
// before
const perPage = searchParams.get('perPage'); // '20' (string)
await fetch(`/api/workflows/${id}/runs?perPage=${perPage}`);
// after
const perPage = Number.parseInt(searchParams.get('perPage') ?? '', 10);
if (Number.isInteger(perPage) && perPage > 0) {
await fetch(`/api/workflows/${id}/runs?perPage=${perPage}`);
} Defensive patterns
Strategy: validation
Validate before calling
function sanitizePerPage(input) {
const n = typeof input === 'string' ? Number.parseInt(input, 10) : input;
if (!Number.isInteger(n) || n <= 0) return undefined; // let server use default
return n;
} Type guard
function isPositiveInteger(v: unknown): v is number {
return typeof v === 'number' && Number.isInteger(v) && v > 0;
} Try / catch
try {
return await workflow.listWorkflowRuns({ perPage });
} catch (e) {
if (e?.status === 400 && /perPage must be a positive integer/i.test(e?.message ?? '')) {
return await workflow.listWorkflowRuns({ perPage: 10 }); // safe default
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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'.
Related errors
- page must be a non-negative integer
- Workflow ID is required
- Run ID is required
- bad request: ${responseText}
- GitHub cursor must be a positive page number.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ed019bf66d5f5762.
Report an issue: GitHub.