mastra-ai/mastra · error

page value too large

Error message

page value too large

What it means

listMessages rejects page values whose computed offset (page * perPage) exceeds Number.MAX_SAFE_INTEGER / 2, because such offsets would break integer precision and cause severe performance issues in an in-memory store. The limit protects against astronomically large page numbers rather than normal pagination ranges.

Source

Thrown at packages/core/src/storage/domains/memory/inmemory.ts:139

    if (threadIds.length === 0 || threadIds.some(id => !id.trim())) {
      throw new Error('threadId must be a non-empty string or array of non-empty strings');
    }

    const threadIdSet = new Set(threadIds);

    const { field, direction } = this.parseOrderBy(orderBy, 'ASC');

    // Normalize perPage for query (false → MAX_SAFE_INTEGER, 0 → 0, undefined → 40)
    const perPage = normalizePerPage(perPageInput, 40);

    if (page < 0) {
      throw new Error('page must be >= 0');
    }

    // Prevent unreasonably large page values that could cause performance issues
    const maxOffset = Number.MAX_SAFE_INTEGER / 2;
    if (page * perPage > maxOffset) {
      throw new Error('page value too large');
    }

    // Calculate offset from page
    const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);

    // When perPage is 0 with no includes, there's nothing to return.
    if (perPage === 0 && (!include || include.length === 0)) {
      return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
    }

    // Step 1: Get messages matching threadId(s) and optionally resourceId
    let threadMessages = Array.from(this.db.messages.values()).filter((msg: any) => {
      // Message must be in one of the specified threads
      if (threadIdSet && !threadIdSet.has(msg.thread_id)) return false;
      // If optionalResourceId provided, message must match it
      if (optionalResourceId && msg.resourceId !== optionalResourceId) return false;
      return true;
    });

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Clamp page to a sane upper bound for your application (e.g. page < 1_000_000) before calling.
  2. Sanitize external pagination input: reject non-finite numbers and cap the value at the API boundary.
  3. Prefer cursor/keyset pagination for very deep datasets instead of huge page offsets.

Example fix

// before
await storage.listMessages({ threadId, page: Number(req.query.page) });
// after
const page = Math.min(Math.max(0, Number(req.query.page) || 0), 1_000_000);
await storage.listMessages({ threadId, page });
Defensive patterns

Strategy: validation

Validate before calling

const MAX_PAGE = 1_000_000;
const page = Math.min(Math.max(0, Number(rawPage) || 0), MAX_PAGE);
await storage.listMessages({ threadId, page });

Type guard

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

Prevention

When it happens

Trigger: Calling listMessages with an absurd page number (e.g. page: Number.MAX_SAFE_INTEGER or a DoS-style crafted query param) combined with any perPage, such that page * perPage > MAX_SAFE_INTEGER / 2.

Common situations: Unvalidated API/query-string input forwarded straight to storage; attacker-supplied pagination params; tests probing boundary conditions.

Related errors


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