{"record":{"id":"32beb75077ac3295","repo":"oven-sh/bun","slug":"missing-groupid","errorCode":null,"errorMessage":"Missing groupId","messagePattern":"Missing groupId","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/associate-issue-with-sentry.ts","lineNumber":29,"sourceCode":"  throw new Error(\"Missing sentry_id\");\n}\nconst sentryId = body.slice(id + \"<!-- sentry_id: \".length, endIdLine).trim();\nif (!sentryId) {\n  throw new Error(\"Missing sentry_id\");\n}\n\nconst response = await fetch(`https://sentry.io/api/0/organizations/4507155222364160/eventids/${sentryId}/`, {\n  headers: {\n    Authorization: `Bearer ${SENTRY_AUTH_TOKEN}`,\n  },\n});\nif (!response.ok) {\n  throw new Error(`Failed to fetch Sentry event: ${response.statusText}`);\n}\nconst json = await response.json();\nconst groupId = json?.groupId;\nif (!groupId) {\n  throw new Error(\"Missing groupId\");\n}\n\nconst issueResponse = await fetch(`https://sentry.io/api/0/issues/${groupId}/`, {\n  headers: {\n    Authorization: `Bearer ${SENTRY_AUTH_TOKEN}`,\n  },\n});\nif (!issueResponse.ok) {\n  throw new Error(`Failed to fetch Sentry issue: ${issueResponse.statusText}`);\n}\nconst { shortId, permalink } = await issueResponse.json();\nif (!shortId || !permalink) {\n  throw new Error(\"Missing shortId or permalink\");\n}\n\nconsole.log(`Sentry ID: ${shortId}`);\nconsole.log(`Sentry permalink: ${permalink}`);\n","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/scripts/associate-issue-with-sentry.ts#L11-L47","documentation":"The event lookup succeeded (2xx) but the parsed JSON has no groupId field. Sentry assigns each event to an issue group; a missing groupId means the payload shape changed or the event has no associated group yet, so the follow-up issue lookup cannot proceed.","triggerScenarios":"Sentry API returns a success envelope without grouping info (event ingested but not grouped, or a shape change in the eventids endpoint); json is an error object rather than an event.","commonSituations":"Upstream Sentry API contract drift; polling an event immediately after ingestion before grouping completes.","solutions":["Log the full response body to see what Sentry actually returned","Retry once after a short delay if grouping may still be in flight","Pin or adapt to the documented response shape for the eventids endpoint"],"exampleFix":"// before\nconst groupId = json?.groupId;\nif (!groupId) throw new Error('Missing groupId');\n\n// after\nconst groupId = json?.groupId;\nif (!groupId) {\n  throw new Error(`Missing groupId in response: ${JSON.stringify(json).slice(0, 200)}`);\n}","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"function hasGroupId(json: unknown): json is { groupId: string } {\n  return typeof json === 'object' && json !== null && typeof (json as any).groupId === 'string' && (json as any).groupId.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Narrow API JSON before use instead of assuming field presence","Log the raw payload once when narrowing fails to catch schema drift early"],"tags":["sentry","api","json","schema"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}