mastra-ai/mastra · error

perPage must be >= 0

Error message

perPage must be >= 0

What it means

validatePagination rejects a perPage argument that is negative, non-finite, or not a safe integer. perPage of 0 is explicitly allowed and returns empty results; any negative value throws. This guards the offset computation page * perPage.

Source

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

        throw new Error(`Metadata key "${key}" exceeds maximum length of ${MAX_METADATA_KEY_LENGTH} characters.`);
      }
    }
  }

  /**
   * Validates pagination parameters and returns safe offset.
   * @param page - Page number (0-indexed)
   * @param perPage - Items per page (0 is allowed and returns empty results)
   * @throws Error if page is negative, perPage is negative/invalid, or offset would overflow
   */
  protected validatePagination(page: number, perPage: number): void {
    if (!Number.isFinite(page) || !Number.isSafeInteger(page) || page < 0) {
      throw new Error('page must be >= 0');
    }

    // perPage: 0 is allowed (returns empty results), negative values are rejected
    if (!Number.isFinite(perPage) || !Number.isSafeInteger(perPage) || perPage < 0) {
      throw new Error('perPage must be >= 0');
    }

    // Skip overflow check when perPage is 0 (no offset needed)
    if (perPage === 0) {
      return;
    }

    // Prevent overflow when calculating offset
    const offset = page * perPage;
    if (!Number.isSafeInteger(offset) || offset > Number.MAX_SAFE_INTEGER) {
      throw new Error('page value too large');
    }
  }

  /**
   * Validates pagination input before normalization.
   * Use this when accepting raw perPageInput (number | false) from callers.
   *

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Clamp perPage: Math.max(0, Math.floor(Number(perPage))).
  2. Use perPage: false to fetch all records instead of a sentinel negative value.
  3. Validate with Number.isSafeInteger(perPage) && perPage >= 0 before calling.
  4. Fix the pageSize derivation so it cannot go negative.

Example fix

// before
await storage.listThreads({ page: 0, perPage: total - offset }); // can be negative
// after
const perPage = Math.max(0, total - offset);
await storage.listThreads({ page: 0, perPage });
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

try {
  await storage.listThreads({ page, perPage: safePerPage(rawPerPage) });
} catch (e) {
  if (e instanceof Error && e.message === 'perPage must be >= 0') {
    return storage.listThreads({ page, perPage: 10 });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling listThreads/listMessages with { perPage: -10 }, { perPage: NaN }, or { perPage: 2.5 }; computing perPage from a difference (pageSize - skipped) that went negative.

Common situations: Config values read from env as negative numbers; 'show all' logic accidentally producing -1; division/subtraction bugs when deriving page size from remaining items.

Related errors


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