{"record":{"id":"0d561bdfcac6d365","repo":"koala73/worldmonitor","slug":"sentry-issues-request-failed-http-response-status-response","errorCode":null,"errorMessage":"Sentry issues request failed: HTTP ${response.status} ${response.statusText}","messagePattern":"Sentry issues request failed: HTTP (.+?) (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/audit-sentry-resolve-pins.mjs","lineNumber":237,"sourceCode":"export async function fetchResolvedIssues(token, org, project, fetchImpl = globalThis.fetch) {\n  const issues = [];\n  let url = issuesUrl(org, project);\n\n  for (let page = 0; page < MAX_PAGES; page += 1) {\n    const response = await fetchImpl(url, {\n      headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n      signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n    });\n    if (response.status === 403) {\n      throw new Error(\n        'Sentry returned 403 for the issues endpoint. A release-scoped `sntrys_` upload '\n          + 'token cannot read issues, no matter which project it was minted for. Export a '\n          + '`sntryu_` user token carrying `event:read` and `project:read` as SENTRY_AUTH_TOKEN '\n          + '(or SENTRY_SESSION_TOKEN) and rerun.',\n      );\n    }\n    if (!response.ok) {\n      throw new Error(`Sentry issues request failed: HTTP ${response.status} ${response.statusText}`);\n    }\n    const batch = await response.json();\n    if (!Array.isArray(batch)) throw new Error('Sentry issues response was not an array');\n    issues.push(...batch);\n\n    const { next } = parseLinkHeader(response.headers.get('link'));\n    if (!next) {\n      assertLiveBoardIsNotEmpty(issues, org, project);\n      return issues;\n    }\n    url = sameOriginCursor(next);\n  }\n\n  throw new Error(`Sentry issues pagination exceeded ${MAX_PAGES} pages`);\n}\n\nasync function main() {\n  const args = parseArguments(process.argv.slice(2));","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/audit-sentry-resolve-pins.mjs#L219-L255","documentation":"After the specific 403 check, fetchResolvedIssues() requires every Sentry HTTP response to be ok (`response.ok`). Any other non-2xx status from the issues endpoint (401 unauthorized, 404 wrong org/project, 429 rate limited, 5xx) surfaces as this generic message embedding the status code and statusText from Sentry's API.","triggerScenarios":"Any page of the paginated GET to `/api/0/projects/{org}/{project}/issues/` returns a status other than 2xx and other than 403 — e.g. 401 (bad/expired token), 404 (org/project slug not found), 429 (rate limited), or 5xx (Sentry outage).","commonSituations":"Typo in SENTRY_ORG or SENTRY_PROJECT slugs (404); SENTRY_HOST pointed at a wrong/self-hosted URL; expired or revoked token (401); hammering the API and hitting 429; transient Sentry 5xx during an incident.","solutions":["Read the HTTP status in the message: 401 → re-mint/fix the `sntryu_` token; 404 → check SENTRY_ORG and SENTRY_PROJECT slugs against the Sentry URL; 429 → retry after backoff; 5xx → retry later or check Sentry status.","Confirm SENTRY_HOST (or the default) points at the correct Sentry instance (sentry.io vs self-hosted).","Verify org/project slugs by opening the project page in Sentry's UI and copying them exactly.","For persistent failures, run offline: `node scripts/audit-sentry-resolve-pins.mjs --input <saved-issues.json>`."],"exampleFix":"// before\nSENTRY_PROJECT=world-monitor  # wrong slug\n// after\nSENTRY_PROJECT=worldmonitor  # exact slug from the Sentry project URL","handlingStrategy":"retry","validationCode":"// preflight the endpoint before the paginated run\nconst probe = await fetch(`${SENTRY_HOST}/api/0/`, {\n  headers: { Authorization: `Bearer ${token}` },\n});\nif (!probe.ok) throw new Error(`Sentry preflight failed: HTTP ${probe.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  issues = await fetchResolvedIssues(token, org, project);\n} catch (err) {\n  if (/HTTP 42[09]/.test(err.message)) {\n    // 429/4205: back off and retry once\n    await new Promise(r => setTimeout(r, 30_000));\n    issues = await fetchResolvedIssues(token, org, project);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Copy org/project slugs directly from the Sentry project URL instead of typing them.","Pin SENTRY_HOST explicitly in CI to avoid wrong-instance mistakes.","Watch Sentry rate limits and add spacing between repeated audit runs.","Check the message's embedded status code first — it usually names the exact fix (401 token, 404 slug, 429 rate)."],"tags":["sentry","http","api","network"],"backgroundTag":"http-error-response","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}