koala73/worldmonitor · error · Error

Sentry issues pagination exceeded ${MAX_PAGES} pages

Error message

Sentry issues pagination exceeded ${MAX_PAGES} pages

What it means

fetchResolvedIssues() follows the `Link` header cursor returned by Sentry, but caps the loop at MAX_PAGES = 50 to protect against runaway pagination. If 50 pages still yield a `next` cursor, the loop exits and this error is thrown, treating unbounded pagination as a malfunction rather than iterating forever.

Solutions

  1. Inspect the cursors being followed — if the same cursor repeats, a proxy or cache is interfering; bypass it or fix SENTRY_HOST.
  2. Narrow the query by exporting a smaller statsPeriod or adjusting the `query` param in issuesUrl() if the org genuinely has >50 pages.
  3. Save an intermediate payload and audit offline with `--input` instead of paginating live.
  4. If legitimate, raise MAX_PAGES in scripts/audit-sentry-resolve-pins.mjs:62.

Example fix

// before (scripts/audit-sentry-resolve-pins.mjs:62)
const MAX_PAGES = 50;
// after
const MAX_PAGES = 200;
Defensive patterns

Strategy: validation

Validate before calling

// sanity-check the page size needed before paginating
const PAGE_SIZE = 100;
const estimated = await fetch(`${SENTRY_HOST}/api/0/projects/${org}/${project}/issues/?query=is:resolved&limit=1`, { headers: { Authorization: `Bearer ${token}` } });
// If Link headers suggest more than MAX_PAGES * PAGE_SIZE issues, narrow the query first.
if (MAX_PAGES * PAGE_SIZE < 5000) throw new Error('Query too broad; narrow statsPeriod or raise MAX_PAGES');

Prevention

When it happens

Trigger: 50 consecutive pages of the issues endpoint each return a `Link: rel="next"` cursor — e.g. a misbehaving cursor loop (same cursor repeated), a proxy stripping the query so the filter never applies, or a genuinely huge resolved-issue count with PAGE_SIZE pages exceeding 50.

Common situations: A gateway caching or rewriting responses so the cursor never advances; a bogus/self-hosted endpoint echoing the same Link header; unusually large org where `is:resolved` with an empty statsPeriod spans thousands of issues.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/54702729cf43bd63. Report an issue: GitHub.

Appendix: source

Thrown at scripts/audit-sentry-resolve-pins.mjs:251

          + '(or SENTRY_SESSION_TOKEN) and rerun.',
      );
    }
    if (!response.ok) {
      throw new Error(`Sentry issues request failed: HTTP ${response.status} ${response.statusText}`);
    }
    const batch = await response.json();
    if (!Array.isArray(batch)) throw new Error('Sentry issues response was not an array');
    issues.push(...batch);

    const { next } = parseLinkHeader(response.headers.get('link'));
    if (!next) {
      assertLiveBoardIsNotEmpty(issues, org, project);
      return issues;
    }
    url = sameOriginCursor(next);
  }

  throw new Error(`Sentry issues pagination exceeded ${MAX_PAGES} pages`);
}

async function main() {
  const args = parseArguments(process.argv.slice(2));
  const org = process.env.SENTRY_ORG || DEFAULT_ORG;
  const project = process.env.SENTRY_PROJECT || DEFAULT_PROJECT;

  let issues;
  if (args.input) {
    issues = JSON.parse(readFileSync(args.input, 'utf8'));
  } else {
    const token = process.env.SENTRY_SESSION_TOKEN || process.env.SENTRY_AUTH_TOKEN;
    if (!token) {
      throw new Error(
        'Set SENTRY_AUTH_TOKEN (or SENTRY_SESSION_TOKEN) to a `sntryu_` user token with '
          + '`event:read` and `project:read`, or pass --input <path> to audit a saved payload.',
      );
    }

View on GitHub (pinned to 7d06c8633d)