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
- Fetch the failing eventtype URL directly and inspect the GeoJSON for the offending feature/geometry.
- Retry — GDACS feeds intermittently serve maintenance or error pages.
- Loosen/extend the accepted geometry.type list if GDACS added a legitimate new geometry kind.
- 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
- Validate GDACS GeoJSON against a fixture in CI to detect schema drift
- Drop-and-warn unknown geometry kinds instead of discarding the whole type
- Detect HTML error pages (non-JSON or missing features) distinctly from schema changes
- Track GDACS API announcements for geometry/field changes
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- EONET malformed event geometry
- ECCC_MALFORMED_PAGE
- EONET malformed event category
- GDACS SEARCH page incomplete (VO)
- GDACS unavailable
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)