koala73/worldmonitor · error · Error
EONET ${res.status}
Error message
EONET ${res.status} What it means
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.
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.
Example fix
// before
const res = await fetchFn(url, { headers, signal: AbortSignal.timeout(15_000) });
if (!res.ok) throw new Error(`EONET ${res.status}`);
// after
let res;
for (let attempt = 0; attempt < 3; attempt++) {
res = await fetchFn(url, { headers, signal: AbortSignal.timeout(15_000) });
if (res.ok) break;
if (res.status < 500 && res.status !== 429) break;
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
}
if (!res.ok) throw new Error(`EONET ${res.status}`); Defensive patterns
Strategy: retry
Validate before calling
// Verify EONET reachability before the seed:
const ping = await fetch(`${EONET_API_URL}?status=open&days=1`, { headers: { Accept: 'application/json' } });
if (!ping.ok) console.warn(`EONET unreachable (${ping.status}); events seed will fail or degrade`); Try / catch
try {
const events = await fetchEonet(days);
} catch (err) {
const status = Number(err.message.replace('EONET ', ''));
if (Number.isFinite(status) && (status >= 500 || status === 429)) {
// EONET outage: retry with backoff, then degrade
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- HTTP ${resp.status}
- CF Radar DDoS API error: protocol=${protocolResp.status} vec
- CF Radar traffic anomalies API error: ${resp.status}
- EONET malformed response
- Redis HTTP ${resp.status}
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/a31754a248991353.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-natural-events.mjs:95
const NATURAL_EVENT_CATEGORIES = new Set([
'severeStorms', 'wildfires', 'volcanoes', 'earthquakes', 'floods',
'landslides', 'drought', 'dustHaze', 'snow', 'tempExtremes',
'seaLakeIce', 'waterColor', 'manmade',
]);
function normalizeCategory(id) {
const c = String(id || '').trim();
return NATURAL_EVENT_CATEGORIES.has(c) ? c : 'manmade';
}
async function fetchEonet(days, fetchFn = globalThis.fetch) {
const url = `${EONET_API_URL}?status=open&days=${days}`;
const res = await fetchFn(url, {
headers: { Accept: 'application/json', 'User-Agent': CHROME_UA },
signal: AbortSignal.timeout(15_000),
});
if (!res.ok) throw new Error(`EONET ${res.status}`);
const data = await res.json();
if (!Array.isArray(data?.events)) throw new Error('EONET malformed response');
const events = [];
const now = Date.now();
for (const event of data.events || []) {
const category = event.categories?.[0];
if (!category) continue;
const normalizedCategory = normalizeCategory(category.id);
if (normalizedCategory === 'earthquakes') continue;
const latestGeo = event.geometry?.[event.geometry.length - 1];
if (!latestGeo || latestGeo.type !== 'Point') continue;
const eventDate = new Date(latestGeo.date);
const [lon, lat] = latestGeo.coordinates;
View on GitHub (pinned to 7d06c8633d)