koala73/worldmonitor · error · Error
GDACS ${res.status}
Error message
GDACS ${res.status} What it means
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.
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
Example fix
// before
const res = await fetchFn(GDACS_API, {...});
if (!res.ok) throw new Error(`GDACS ${res.status}`);
// after
const res = await fetchFn(GDACS_API, {...});
if (!res.ok) {
if (res.status === 429 || res.status >= 500) {
await sleep(backoffMs); // retry transient failures
return fetchGdacs(fetchFn);
}
throw new Error(`GDACS ${res.status} ${res.statusText}`);
} Defensive patterns
Strategy: retry
Validate before calling
// before calling fetchGdacs
const head = await fetchFn(GDACS_API, { method: 'HEAD', headers: { 'User-Agent': CHROME_UA } });
if (!head.ok) console.warn(`GDACS unavailable (HTTP ${head.status}), deferring seed`); Try / catch
try {
const events = await fetchGdacs();
} catch (err) {
if (/^GDACS \d{3}$/.test(err.message)) {
const status = Number(err.message.split(' ')[1]);
if (status === 429 || status >= 500) await retryWithBackoff(() => fetchGdacs());
else console.error(`GDACS permanently unavailable: HTTP ${status}`);
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- Redis transaction failed: HTTP ${resp.status} — ${text.slice
- Redis HTTP ${resp.status}
- HTTP ${response.status}
- HTTP ${response.status}
- Sentry issues request failed: HTTP ${response.status} ${resp
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/5da0cb68aaf32a6d.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-natural-events.mjs:216
break;
}
}
const pressureMatch = desc.match(/(\d{3,4})\s*(?:mb|hPa|mbar)/i);
if (pressureMatch) {
const p = parseInt(pressureMatch[1], 10);
if (p >= 850 && p <= 1050) fields.pressureMb = p;
}
return fields;
}
async function fetchGdacs(fetchFn = globalThis.fetch) {
const res = await fetchFn(GDACS_API, {
headers: { Accept: 'application/json', 'User-Agent': CHROME_UA },
signal: AbortSignal.timeout(15_000),
});
if (!res.ok) throw new Error(`GDACS ${res.status}`);
const data = await res.json();
if (!Array.isArray(data?.features)) throw new Error('GDACS malformed response');
const features = data.features;
const seen = new Set();
const events = [];
for (const f of features) {
if (!f.geometry || f.geometry.type !== 'Point') continue;
const props = f.properties;
const key = `${props.eventtype}-${props.eventid}`;
if (seen.has(key)) continue;
seen.add(key);
if (props.alertlevel === 'Green') continue;
const category = GDACS_TO_CATEGORY[props.eventtype] || 'manmade';
const alertPrefix = props.alertlevel === 'Red' ? '\u{1F534} ' : props.alertlevel === 'Orange' ? '\u{1F7E0} ' : '';View on GitHub (pinned to 7d06c8633d)