koala73/worldmonitor · critical

GDACS unavailable

Error message

GDACS unavailable: ${failedTypes.map((t) => t.message).join('; ')}

What it means

Aggregate failure thrown by fetchGdacs: every requested GDACS event type's fetch was rejected and no fallback snapshot (from previousSources) was available for any type. The message concatenates each per-type failure message, so it reports all underlying causes at once.

Solutions

  1. Read the joined per-type messages in the error to identify the common cause (all network vs all schema failures).
  2. Check GDACS service status and test one feed URL directly with curl.
  3. Retry with backoff — the all-types failure is usually transient outage or rate limiting.
  4. Verify network/proxy/egress configuration in the environment running the seed.

Example fix

// before
if (failedTypes.length === types.length && !Object.values(snapshots).some(Boolean)) {
  throw new Error(`GDACS unavailable: ${failedTypes.map((t) => t.message).join('; ')}`);
}
// after
if (failedTypes.length === types.length && !Object.values(snapshots).some(Boolean)) {
  throw new Error(`GDACS unavailable: ${failedTypes.map((t) => t.message).join('; ')}`); // inspect per-type messages above
}
Defensive patterns

Strategy: retry

Validate before calling

const probe = await fetch('https://www.gdacs.org/gdacsapi/api/events/getevents/SEARCH'); if (!probe.ok) console.warn(`GDACS unreachable: HTTP ${probe.status}`);

Try / catch

try { const gdacs = await fetchGdacs(fetchFn, { previousSources }); } catch (e) { if (e.message.startsWith('GDACS unavailable:')) { console.error('All GDACS types failed:', e.message); // schedule retry with backoff } throw e; }

Prevention

When it happens

Trigger: All types in the fetchGdacs type list fail (fetchGdacsType threw for each — network errors, malformed responses, pagination errors) AND every snapshots[source] entry is null/undefined.

Common situations: GDACS is fully down or blocking the seeder's IP/WAF; total network outage in CI; a GDACS API endpoint migration breaking all type feeds simultaneously; credentials/proxy misconfiguration cutting all outbound requests.

Related errors


AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-22). Data as JSON: /api/errors/ed75cf7a4b4b8490. Report an issue: GitHub.

Appendix: source

Thrown at scripts/seed-natural-events.mjs:406

const GDACS_ALERT_RANK = { Red: 0, Orange: 1 };

export async function fetchGdacs(fetchFn = globalThis.fetch, { previousSources = null, now = Date.now() } = {}) {
  const types = Object.keys(GDACS_TO_CATEGORY);
  const settled = await Promise.allSettled(types.map((eventtype) => fetchGdacsType(eventtype, fetchFn, now)));
  const failedTypes = [];
  const features = [];
  const snapshots = {};
  settled.forEach((result, i) => {
    const type = types[i];
    const source = `gdacs:${type}`;
    const snapshot = selectSourceSnapshot(result, previousSources?.[source], now, records => validGdacsFeatures(records, type));
    snapshots[source] = snapshot;
    if (snapshot) features.push(...snapshot.records);
    if (result.status === 'rejected') failedTypes.push({ eventtype: type, message: result.reason?.message || String(result.reason) });
  });
  if (failedTypes.length === types.length && !Object.values(snapshots).some(Boolean)) {
    throw new Error(`GDACS unavailable: ${failedTypes.map((t) => t.message).join('; ')}`);
  }

  // The 100-event cap used to fall on GDACS's own mixed-type ordering. The
  // per-type lists arrive grouped, so capping the raw concatenation would let
  // 100 earthquakes evict every cyclone, including the TC members the
  // western-Pacific snapshot is built from. Rank by alert level, then newest
  // first, so the cap drops the least urgent events regardless of type.
  const rank = (f) => GDACS_ALERT_RANK[f?.properties?.alertlevel] ?? 2;
  const started = (f) => new Date(f?.properties?.fromdate || 0).getTime() || 0;
  features.sort((a, b) => rank(a) - rank(b) || started(b) - started(a));

  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}`;

View on GitHub (pinned to e586b8b4b8)