{"record":{"id":"a31754a248991353","repo":"koala73/worldmonitor","slug":"eonet-res-status","errorCode":null,"errorMessage":"EONET ${res.status}","messagePattern":"EONET \\$\\{res\\.status\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/seed-natural-events.mjs","lineNumber":95,"sourceCode":"\nconst NATURAL_EVENT_CATEGORIES = new Set([\n  'severeStorms', 'wildfires', 'volcanoes', 'earthquakes', 'floods',\n  'landslides', 'drought', 'dustHaze', 'snow', 'tempExtremes',\n  'seaLakeIce', 'waterColor', 'manmade',\n]);\n\nfunction normalizeCategory(id) {\n  const c = String(id || '').trim();\n  return NATURAL_EVENT_CATEGORIES.has(c) ? c : 'manmade';\n}\n\nasync function fetchEonet(days, fetchFn = globalThis.fetch) {\n  const url = `${EONET_API_URL}?status=open&days=${days}`;\n  const res = await fetchFn(url, {\n    headers: { Accept: 'application/json', 'User-Agent': CHROME_UA },\n    signal: AbortSignal.timeout(15_000),\n  });\n  if (!res.ok) throw new Error(`EONET ${res.status}`);\n\n  const data = await res.json();\n  if (!Array.isArray(data?.events)) throw new Error('EONET malformed response');\n  const events = [];\n  const now = Date.now();\n\n  for (const event of data.events || []) {\n    const category = event.categories?.[0];\n    if (!category) continue;\n    const normalizedCategory = normalizeCategory(category.id);\n    if (normalizedCategory === 'earthquakes') continue;\n\n    const latestGeo = event.geometry?.[event.geometry.length - 1];\n    if (!latestGeo || latestGeo.type !== 'Point') continue;\n\n    const eventDate = new Date(latestGeo.date);\n    const [lon, lat] = latestGeo.coordinates;\n","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/seed-natural-events.mjs#L77-L113","documentation":"fetchEonet (scripts/seed-natural-events.mjs) queries NASA's EONET open-events feed (`?status=open&days=N`) with a Chrome User-Agent and a 15s timeout, and throws `EONET ${res.status}` when `res.ok` is false. Any non-2xx from EONET — 403 for throttling, 5xx for NASA-side incidents — aborts the natural-events seed. The message is status-only, so the response body (often an HTML error page from NASA gateways) is discarded.","triggerScenarios":"GET ${EONET_API_URL}?status=open&days=${days} returns a non-2xx status (EONET service outage 5xx, gateway 502/503/504, occasional 403 throttling), evaluated by `if (!res.ok) throw` after the fetchFn call resolves.","commonSituations":"NASA EONET API downtime or maintenance (it has periodic outages); corporate/CI egress proxy returning 403/502; transient DNS or gateway blips during scheduled seed runs.","solutions":["Check the status code: 5xx/502/503/504 → EONET-side outage; retry with exponential backoff (2-3 attempts) before failing the seed.","Open the URL in a browser or curl it to confirm whether the failure is environmental (proxy, DNS) vs. NASA-side.","403 from a proxy: run from an allowed network or add proxy config to the fetch environment.","Add a degraded path: skip the EONET dataset for this run and record degraded health instead of aborting the whole seed."],"exampleFix":"// before\nconst res = await fetchFn(url, { headers, signal: AbortSignal.timeout(15_000) });\nif (!res.ok) throw new Error(`EONET ${res.status}`);\n// after\nlet res;\nfor (let attempt = 0; attempt < 3; attempt++) {\n  res = await fetchFn(url, { headers, signal: AbortSignal.timeout(15_000) });\n  if (res.ok) break;\n  if (res.status < 500 && res.status !== 429) break;\n  await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));\n}\nif (!res.ok) throw new Error(`EONET ${res.status}`);","handlingStrategy":"retry","validationCode":"// Verify EONET reachability before the seed:\nconst ping = await fetch(`${EONET_API_URL}?status=open&days=1`, { headers: { Accept: 'application/json' } });\nif (!ping.ok) console.warn(`EONET unreachable (${ping.status}); events seed will fail or degrade`);","typeGuard":null,"tryCatchPattern":"try {\n  const events = await fetchEonet(days);\n} catch (err) {\n  const status = Number(err.message.replace('EONET ', ''));\n  if (Number.isFinite(status) && (status >= 500 || status === 429)) {\n    // EONET outage: retry with backoff, then degrade\n  } else throw err;\n}","preventionTips":["Build in 2-3 retries with exponential backoff for EONET, which has frequent transient outages.","Send a User-Agent (the script already does) to avoid UA-based 403s at gateways.","Degrade gracefully: skip natural-events seeding and mark health degraded rather than aborting.","Watch for 502/503/504 patterns that indicate NASA gateway issues, not your config."],"tags":["http","eonet","nasa","seed-script","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"}