mastra-ai/mastra · error

perPage must be false or a safe integer

Error message

perPage must be false or a safe integer

What it means

validatePaginationInput accepts perPage as a number or the literal false (meaning 'fetch all'). If perPageInput is neither false nor a finite safe integer, this error is thrown. Strings, null, undefined, Infinity, and NaN are all rejected.

Source

Thrown at packages/core/src/storage/domains/memory/base.ts:491

  /**
   * Validates pagination input before normalization.
   * Use this when accepting raw perPageInput (number | false) from callers.
   *
   * When perPage is false (fetch all), page must be 0 since pagination is disabled.
   * When perPage is a number, delegates to validatePagination for full validation.
   *
   * @param page - Page number (0-indexed)
   * @param perPageInput - Items per page as number, or false to fetch all results
   * @throws Error if perPageInput is false and page !== 0
   * @throws Error if perPageInput is invalid (not false or a non-negative safe integer)
   * @throws Error if page is invalid or offset would overflow
   */
  protected validatePaginationInput(page: number, perPageInput: number | false): void {
    // Validate perPageInput type first
    if (perPageInput !== false) {
      if (typeof perPageInput !== 'number' || !Number.isFinite(perPageInput) || !Number.isSafeInteger(perPageInput)) {
        throw new Error('perPage must be false or a safe integer');
      }
      if (perPageInput < 0) {
        throw new Error('perPage must be >= 0');
      }
    }

    // When fetching all (perPage: false), only page 0 is valid
    if (perPageInput === false) {
      if (page !== 0) {
        throw new Error('page must be 0 when perPage is false');
      }
      // Still validate page is a valid integer
      if (!Number.isFinite(page) || !Number.isSafeInteger(page)) {
        throw new Error('page must be >= 0');
      }
      return;
    }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Parse and coerce: const perPage = raw === false ? false : Number.parseInt(raw, 10).
  2. Validate with (perPage === false || (Number.isSafeInteger(perPage) && perPage >= 0)) before calling.
  3. Use perPage: false explicitly (not null/undefined) when you want all records.
  4. Enable strict TypeScript checking so string|null values cannot reach the call site.

Example fix

// before
await storage.listThreads({ page: 0, perPage: req.query.perPage }); // string
// after
const raw = req.query.perPage;
const perPage = raw === 'all' ? false : Math.max(0, parseInt(raw, 10) || 0);
await storage.listThreads({ page: 0, perPage });
Defensive patterns

Strategy: type-guard

Validate before calling

function normalizePerPage(raw) {
  if (raw === false) return false;
  const n = Number(raw);
  return Number.isSafeInteger(n) && n >= 0 ? n : 10;
}

Type guard

function isValidPerPageInput(p: unknown): p is number | false {
  return p === false || (typeof p === 'number' && Number.isSafeInteger(p) && p >= 0);
}

Try / catch

try {
  await storage.listThreads({ page: 0, perPage });
} catch (e) {
  if (e instanceof Error && e.message === 'perPage must be false or a safe integer') {
    return storage.listThreads({ page: 0, perPage: 10 });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling listThreads/listMessages with { perPage: '10' } (string from a query param), { perPage: null }, { perPage: undefined } typed as number, or { perPage: Infinity }.

Common situations: Passing unparsed query-string values (req.query always yields strings); JSON configs where perPage came through as null; TypeScript types bypassed via `as any` or JS callers.

Related errors


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