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
- Read the joined per-type messages in the error to identify the common cause (all network vs all schema failures).
- Check GDACS service status and test one feed URL directly with curl.
- Retry with backoff — the all-types failure is usually transient outage or rate limiting.
- 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
- Retry the whole fetchGdacs batch with exponential backoff before giving up
- Serve last-known-good snapshots (previousSources) when all types fail
- Monitor GDACS availability from CI before running scheduled seeds
- Check egress/proxy config — an all-types failure usually means environment, not schema
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
- GDACS malformed response
- GDACS SEARCH page incomplete (VO)
- Sentry issues request failed: HTTP
- bodyTransportFailure ? 'NHC_POINT_REQUEST_FAILED' : 'NHC_POINT_RESPONSE_INVALID'
- Brief service unavailable
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)