mastra-ai/mastra · error · HTTPException

page must be a non-negative integer

Error message

page must be a non-negative integer

What it means

The workflow-runs route validates `page` and throws 400 'page must be a non-negative integer' when page is provided but is negative or not an integer. Zero is allowed (first page); -1, 1.5, or a non-numeric string are rejected. This check runs after the perPage validation and before the runs are fetched from storage.

Source

Thrown at packages/server/src/server/handlers/workflows.ts:392

      // 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,
      };
      return workflowRuns;
    } catch (error) {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Clamp page to >= 0 with Math.max(0, page) before requesting
  2. Use Math.floor/Math.round on computed page values
  3. Parse string query params with Number.parseInt before sending
  4. Omit page to get the default first page

Example fix

// before
const page = Math.ceil(total / perPage) - 1 - 1; // can go negative
// after
const page = Math.max(0, Math.floor(total / perPage) - 1);
Defensive patterns

Strategy: validation

Validate before calling

function sanitizePage(input) {
  const n = typeof input === 'string' ? Number.parseInt(input, 10) : input;
  if (!Number.isInteger(n) || n < 0) return 0;
  return n;
}

Type guard

function isNonNegativeInteger(v: unknown): v is number {
  return typeof v === 'number' && Number.isInteger(v) && v >= 0;
}

Try / catch

try {
  return await workflow.listWorkflowRuns({ page });
} catch (e) {
  if (e?.status === 400 && /page must be a non-negative integer/i.test(e?.message ?? '')) {
    return await workflow.listWorkflowRuns({ page: 0 });
  }
  throw e;
}

Prevention

When it happens

Trigger: GET /api/workflows/:id/runs?page=-1; computing page as currentPage-1 when already on page 0 (yielding -1); passing a float from custom pagination math; sending page as an unparsed string.

Common situations: Client-side 'previous page' buttons not clamped at 0; fractional pages from dividing total items by perPage and passing the raw quotient; string query params forwarded without parsing.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/755fbcbfce6d4779. Report an issue: GitHub.