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

  1. 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
  2. Verify the GDACS_API constant still points at the live endpoint (open it in a browser or curl -I with the same headers)
  3. Check for 403: some GDACS edges block automation; keep a realistic User-Agent (CHROME_UA is already set) and add an Accept header
  4. If 429, stagger requests or reduce seeding frequency; respect any Retry-After header
  5. 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

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


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)