{"record":{"id":"5da0cb68aaf32a6d","repo":"koala73/worldmonitor","slug":"gdacs-res-status","errorCode":null,"errorMessage":"GDACS ${res.status}","messagePattern":"GDACS \\$\\{res\\.status\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/seed-natural-events.mjs","lineNumber":216,"sourceCode":"      break;\n    }\n  }\n\n  const pressureMatch = desc.match(/(\\d{3,4})\\s*(?:mb|hPa|mbar)/i);\n  if (pressureMatch) {\n    const p = parseInt(pressureMatch[1], 10);\n    if (p >= 850 && p <= 1050) fields.pressureMb = p;\n  }\n\n  return fields;\n}\n\nasync function fetchGdacs(fetchFn = globalThis.fetch) {\n  const res = await fetchFn(GDACS_API, {\n    headers: { Accept: 'application/json', 'User-Agent': CHROME_UA },\n    signal: AbortSignal.timeout(15_000),\n  });\n  if (!res.ok) throw new Error(`GDACS ${res.status}`);\n\n  const data = await res.json();\n  if (!Array.isArray(data?.features)) throw new Error('GDACS malformed response');\n  const features = data.features;\n  const seen = new Set();\n  const events = [];\n\n  for (const f of features) {\n    if (!f.geometry || f.geometry.type !== 'Point') continue;\n    const props = f.properties;\n    const key = `${props.eventtype}-${props.eventid}`;\n    if (seen.has(key)) continue;\n    seen.add(key);\n\n    if (props.alertlevel === 'Green') continue;\n\n    const category = GDACS_TO_CATEGORY[props.eventtype] || 'manmade';\n    const alertPrefix = props.alertlevel === 'Red' ? '\\u{1F534} ' : props.alertlevel === 'Orange' ? '\\u{1F7E0} ' : '';","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/seed-natural-events.mjs#L198-L234","documentation":"fetchGdacs in scripts/seed-natural-events.mjs calls the GDACS API with a 15s timeout and a Chrome User-Agent, then requires res.ok before parsing. This error is thrown when the GDACS HTTP response has a non-2xx status; the status code is interpolated into the message (e.g. 'GDACS 503'). It aborts the natural-events seeding run for the GDACS dataset because the payload cannot be trusted.","triggerScenarios":"Calling fetchGdacs() (directly or via the seeder's fetch loop) when the GDACS endpoint returns a non-OK status: 5xx during GDACS outages, 429 after rate limiting, 403 from bot/UA filtering, or 404 if the GDACS_API URL constant is wrong or the endpoint path changes.","commonSituations":"GDACS service outages or maintenance windows; hammering the API from CI so a shared-IP rate limit kicks in; corporate proxies returning 403 for the CHROME_UA header; typos or upstream path changes to the GDACS_API constant.","solutions":["Log res.status and res.statusText, then re-run the seeder once GDACS is reachable — a 5xx/429 is usually transient, so retry with exponential backoff","Verify the GDACS_API constant still points at the live endpoint (open it in a browser or curl -I with the same headers)","Check for 403: some GDACS edges block automation; keep a realistic User-Agent (CHROME_UA is already set) and add an Accept header","If 429, stagger requests or reduce seeding frequency; respect any Retry-After header","For persistent non-OK responses, fall back to the cached/seeded events so the dashboard still renders"],"exampleFix":"// before\nconst res = await fetchFn(GDACS_API, {...});\nif (!res.ok) throw new Error(`GDACS ${res.status}`);\n// after\nconst res = await fetchFn(GDACS_API, {...});\nif (!res.ok) {\n  if (res.status === 429 || res.status >= 500) {\n    await sleep(backoffMs); // retry transient failures\n    return fetchGdacs(fetchFn);\n  }\n  throw new Error(`GDACS ${res.status} ${res.statusText}`);\n}","handlingStrategy":"retry","validationCode":"// before calling fetchGdacs\nconst head = await fetchFn(GDACS_API, { method: 'HEAD', headers: { 'User-Agent': CHROME_UA } });\nif (!head.ok) console.warn(`GDACS unavailable (HTTP ${head.status}), deferring seed`);","typeGuard":null,"tryCatchPattern":"try {\n  const events = await fetchGdacs();\n} catch (err) {\n  if (/^GDACS \\d{3}$/.test(err.message)) {\n    const status = Number(err.message.split(' ')[1]);\n    if (status === 429 || status >= 500) await retryWithBackoff(() => fetchGdacs());\n    else console.error(`GDACS permanently unavailable: HTTP ${status}`);\n  } else throw err;\n}","preventionTips":["Keep a realistic User-Agent and Accept header on every GDACS request","Retry 429/5xx with exponential backoff and honor Retry-After","Stagger seed runs to avoid shared-IP rate limits","Monitor the GDACS_API URL for upstream path changes"],"tags":["http","network","upstream-api","seeding"],"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"}