koala73/worldmonitor · error · Error

Expected an array of Sentry issues. A Sentry error body is a

Error message

Expected an array of Sentry issues. A Sentry error body is an object, so coercing this to an empty list would report an authentication failure as a clean board.

What it means

auditResolvedIssues insists its input is an array of Sentry issues. When the Sentry API returns an error body (an object, e.g. {detail: "Invalid token"}), coercing it to an empty array would make a broken API call look like a successfully resolved board. The script throws instead so failures are loud, never silently reported as clean.

Solutions

  1. Check the HTTP status before parsing: only pass await response.json() to auditResolvedIssues when response.ok is true.
  2. Verify the Sentry token is valid and has access to the org/project that produced the error body.
  3. Log the raw response body on non-OK status to see the actual API error (invalid token, wrong slug, rate limit) before auditing.
  4. Restructure the caller so error bodies throw or exit non-zero before reaching auditResolvedIssues.

Example fix

// before
const issues = await res.json();
auditResolvedIssues(issues);
// after
if (!res.ok) throw new Error(`Sentry API ${res.status}: ${JSON.stringify(await res.json())}`);
auditResolvedIssues(await res.json());
Defensive patterns

Strategy: try-catch

Validate before calling

const body = await res.json();
if (!Array.isArray(body)) throw new Error(`Expected Sentry issue list, got: ${JSON.stringify(body).slice(0, 200)}`);

Type guard

const isIssueArray = (v) => Array.isArray(v);

Try / catch

try {
  auditResolvedIssues(issues);
} catch (err) {
  if (err.message.startsWith('Expected an array of Sentry issues')) {
    console.error('Sentry returned an error body instead of an issue list — check token and res.ok before auditing.');
    process.exitCode = 1;
  } else throw err;
}

Prevention

When it happens

Trigger: Passing the parsed JSON of a Sentry HTTP error response (authentication failure, 403, rate-limit body) — or any non-array value such as an object or null — to auditResolvedIssues.

Common situations: An expired or revoked auth token returns {detail: "Authentication credentials were not provided."}; a wrong org/project slug returns an error object; a caller skips checking response.ok and feeds the error body straight to the audit.

Understand the failure class

Related errors


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

Appendix: source

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

  }
  return { next: null };
}

export function classifyResolution(issue) {
  const statusDetails = issue?.statusDetails;
  if (statusDetails && typeof statusDetails === 'object') {
    for (const pin of PIN_KINDS) {
      const value = statusDetails[pin.statusDetailsKey];
      if (value === undefined || value === null) continue;
      return { kind: pin.kind, pinKey: pin.statusDetailsKey, pinValue: pin.describe(value) };
    }
  }
  return { kind: 'plain', pinKey: null, pinValue: null };
}

export function auditResolvedIssues(issues) {
  if (!Array.isArray(issues)) {
    throw new Error(
      'Expected an array of Sentry issues. A Sentry error body is an object, so coercing this '
        + 'to an empty list would report an authentication failure as a clean board.',
    );
  }
  const violations = [];
  let checked = 0;
  let skippedNonResolved = 0;

  for (const issue of issues) {
    if (issue?.status !== 'resolved') {
      skippedNonResolved += 1;
      continue;
    }
    checked += 1;
    const { kind, pinValue } = classifyResolution(issue);
    if (kind === 'plain') continue;
    violations.push({
      shortId: issue.shortId ?? null,

View on GitHub (pinned to 7d06c8633d)