{"record":{"id":"da9b65f58ff3cb24","repo":"koala73/worldmonitor","slug":"get-url-failed-http-response-status-body-body-slice-0-300","errorCode":null,"errorMessage":"GET ${url} failed: HTTP ${response.status}${body ? ` — ${body.slice(0, 300)}` : ''}${response.status === 403 ? ' (need sntryu_ user token with project:read)' : ''}","messagePattern":"GET (.+?) failed: HTTP (.+?)(.+?)` : ''\\}(.+?)","errorType":"exception","errorClass":null,"httpStatus":403,"severity":"error","filePath":"scripts/sync-sentry-convex-probe-filters.mjs","lineNumber":277,"sourceCode":"}\n\n/**\n * GET project options from Sentry (includes `filters:error_messages`).\n *\n * @param {string} token\n * @param {string} org\n * @param {string} project\n * @returns {Promise<Record<string, unknown>>}\n */\nasync function fetchProjectOptions(token, org, project) {\n  const url = `${SENTRY_HOST}/api/0/projects/${org}/${project}/`;\n  const response = await fetch(url, {\n    headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n    signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n  });\n  if (!response.ok) {\n    const body = await response.text();\n    throw new Error(\n      `GET ${url} failed: HTTP ${response.status}`\n        + (body ? ` — ${body.slice(0, 300)}` : '')\n        + (response.status === 403\n          ? ' (need sntryu_ user token with project:read)'\n          : ''),\n    );\n  }\n  return /** @type {Record<string, unknown>} */ (await response.json());\n}\n\n/**\n * PUT `filters:error_messages` on the Sentry project (merge-safe caller must\n * pass the full newline-joined option string).\n *\n * @param {string} token\n * @param {string} org\n * @param {string} project\n * @param {string} filtersText","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/koala73/worldmonitor/blob/e586b8b4b80f595aa7ece295eec10d76f2921240/scripts/sync-sentry-convex-probe-filters.mjs#L259-L295","documentation":"fetchProjectOptions performs an authenticated GET of the Sentry project options and throws when the response is not ok, embedding the status code, up to 300 chars of the response body, and a hint for 403 that a sntryu_ user token with project:read scope is required. It surfaces the real Sentry API failure instead of failing later with a confusing parse error.","triggerScenarios":"Any non-2xx from GET of the Sentry project options endpoint: 401 invalid/expired token, 403 token lacking project:read (or is an org token instead of sntryu_ user token), 404 wrong org/project slug, 429 rate limited.","commonSituations":"Using SENTRY_AUTH_TOKEN from CI (org-level) instead of a user token, missing project:read scope, mistyped SENTRY_ORG/SENTRY_PROJECT, or an expired session token.","solutions":["Create a sntryu_ user token with project:read (and project:write) scope and set it as SENTRY_API_TOKEN.","Verify SENTRY_ORG and SENTRY_PROJECT slugs match the Sentry URL.","Check the embedded response body in the error for Sentry's specific message (rate limit, auth, not found).","If 429, wait and retry after the rate-limit window."],"exampleFix":"// before\nSENTRY_AUTH_TOKEN=sntrys_...  // org token, no project:read\n// after\nSENTRY_API_TOKEN=sntryu_...  // user token with project:read and project:write","handlingStrategy":"try-catch","validationCode":"if (!process.env.SENTRY_API_TOKEN?.startsWith('sntryu_')) throw new Error('Need sntryu_ user token with project:read');","typeGuard":null,"tryCatchPattern":"try { await fetchProjectOptions(...); } catch (err) {\n  if (err.message.includes('HTTP 403')) console.error('Token lacks project:read — create a sntryu_ user token');\n  else if (err.message.includes('HTTP 429')) scheduleRetry();\n  else throw err;\n}","preventionTips":["Use a sntryu_ user token with project:read, not org-level CI tokens.","Verify SENTRY_ORG/SENTRY_PROJECT slugs against the Sentry URL before running.","Dry-run the GET with curl first to confirm token access."],"tags":["http","sentry","authentication","api"],"backgroundTag":"http-error-response","analyzedSha":"e586b8b4b80f595aa7ece295eec10d76f2921240","analyzedAt":"2026-09-22T01:50:49.965Z","contentChangedAt":"2026-09-22T01:50:49.965Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}