koala73/worldmonitor · error

GDACS malformed response

Error message

GDACS malformed response (${eventtype})

What it means

Thrown by fetchGdacsType when the GDACS (Global Disaster Alerts and Coordination System) response for an event type fails shape validation: data.features is not an array, or any feature's geometry.type is outside the accepted set (Point, Polygon, MultiPolygon, LineString, MultiLineString). The same message is reused when validGdacsFeatures rejects the Point subset.

Solutions

  1. Fetch the failing eventtype URL directly and inspect the GeoJSON for the offending feature/geometry.
  2. Retry — GDACS feeds intermittently serve maintenance or error pages.
  3. Loosen/extend the accepted geometry.type list if GDACS added a legitimate new geometry kind.
  4. Verify the request is not being blocked (check for HTML error bodies; keep the browser-like User-Agent).

Example fix

// before
if (!Array.isArray(data?.features) || data.features.some(feature =>
  !['Point', 'Polygon', 'MultiPolygon', 'LineString', 'MultiLineString'].includes(feature?.geometry?.type))) {
  throw new Error(`GDACS malformed response (${eventtype})`);
}
// after
const bad = data?.features?.filter(f => !['Point', 'Polygon', 'MultiPolygon', 'LineString', 'MultiLineString'].includes(f?.geometry?.type)) ?? [];
if (bad.length) console.warn(`GDACS ${eventtype}: skipping ${bad.length} features with unknown geometry`);
Defensive patterns

Strategy: validation

Validate before calling

const res = await fetch(url); const data = await res.json(); const bad = (data?.features || []).filter(f => !['Point','Polygon','MultiPolygon','LineString','MultiLineString'].includes(f?.geometry?.type)); if (bad.length) console.warn(`GDACS features with unknown geometry: ${bad.length}`);

Type guard

function hasKnownGeometry(feature) { return ['Point', 'Polygon', 'MultiPolygon', 'LineString', 'MultiLineString'].includes(feature?.geometry?.type); }

Try / catch

try { const points = await fetchGdacsType(type, fetchFn); } catch (e) { if (/GDACS malformed response/.test(e.message)) { console.error(`GDACS feed for ${type} failed shape validation; inspect raw GeoJSON`); } throw e; }

Prevention

When it happens

Trigger: GDACS GeoJSON feed for an eventtype (e.g. TC, EQ, VO, FL) returns a non-array features field, a feature with null/unknown geometry.type, or the Point records fail validGdacsFeatures checks.

Common situations: GDACS changes its GeoJSON schema; the feed returns an XML/HTML error page parsed loosely; WAF/blocked requests return malformed bodies; new geometry kinds appear for an event type.

Understand the failure class

Related errors


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

Appendix: source

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

  return fields;
}

async function fetchGdacsType(eventtype, fetchFn, now) {
  // MAP requires one type, but its VO route returns 404. SEARCH supplies
  // recent volcano events; their iscurrent field determines closure below.
  const url = new URL(eventtype === 'VO' ? GDACS_API.replace(/MAP$/, 'SEARCH') : GDACS_API);
  if (eventtype === 'VO') {
    url.search = new URLSearchParams({
      eventlist: 'VO',
      fromDate: new Date(now - DAYS * 86_400_000).toISOString().slice(0, 10),
      toDate: new Date(now).toISOString().slice(0, 10),
      pageSize: '100', pageNumber: '1',
    }).toString();
  } else url.searchParams.set('eventtype', eventtype);
  const data = await fetchEventSourceJson(`gdacs:${eventtype}`, url.toString(), fetchFn);
  if (!Array.isArray(data?.features) || data.features.some(feature =>
    !['Point', 'Polygon', 'MultiPolygon', 'LineString', 'MultiLineString'].includes(feature?.geometry?.type))) {
    throw new Error(`GDACS malformed response (${eventtype})`);
  }
  if (eventtype === 'VO' && data.features.length >= 100) throw new Error('GDACS SEARCH page incomplete (VO)');
  // MAP also carries impact polygons and forecast tracks. They are not event
  // records; retaining them would inflate snapshots without adding coverage.
  const points = data.features.filter(feature => feature.geometry.type === 'Point');
  if (!validGdacsFeatures(points, eventtype)) throw new Error(`GDACS malformed response (${eventtype})`);
  return points;
}

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) => {

View on GitHub (pinned to e586b8b4b8)