koala73/worldmonitor · error
EONET malformed event category
Error message
EONET malformed event category
What it means
Thrown by fetchEonet while iterating NASA EONET events: an event has no categories[0], so its category id cannot be normalized. The seeder requires every event to carry a category to classify it (and skip earthquakes), and treats its absence as a malformed payload rather than skipping the event.
Solutions
- Check the raw EONET API response for the offending event (curl the same URL) to confirm categories is missing vs a transient issue.
- Retry the seed — if the API returned a truncated/partial response, a fresh request often fixes it.
- Consider skipping events without a category instead of failing the whole seed, preserving the rest of the batch.
- Verify no intermediate cache/proxy is truncating the JSON payload.
Example fix
// before
const category = event.categories?.[0];
if (!category) throw new Error('EONET malformed event category');
// after
const category = event.categories?.[0];
if (!category) { console.warn('Skipping EONET event without category', event.id); continue; } Defensive patterns
Strategy: type-guard
Validate before calling
const badEvents = (data?.events || []).filter(e => !e?.categories?.[0]); if (badEvents.length) console.warn('EONET events missing category:', badEvents.map(e => e.id)); Type guard
function hasCategory(event) { return Array.isArray(event?.categories) && event.categories.length > 0 && event.categories[0] != null; } Try / catch
try { const events = await fetchEonet(fetchFn); } catch (e) { if (e.message === 'EONET malformed event category') { console.error('EONET payload missing categories; inspect raw response before retrying'); } throw e; } Prevention
- Validate the EONET envelope (events array shape) before the per-event loop
- Skip-and-warn on individual malformed events instead of failing the whole seed
- Compare responses against a saved good snapshot when the API changes
- Watch NASA EONET API changelogs for schema updates
When it happens
Trigger: Any event object in data.events whose categories array is missing, null, or empty (event.categories?.[0] is undefined).
Common situations: EONET API schema change or partial outage returning events without categories; caching/proxy layers serving truncated GeoJSON; a new EONET event type that does not populate categories yet.
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
- EONET malformed event
- GDACS malformed response
- CF Radar DDoS API error: protocol=
- CF Radar traffic anomalies API error
AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-22).
Data as JSON: /api/errors/a953da50486d28ec.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-natural-events.mjs:253
retryAfterMs: cause.retryAfterMs,
});
if (deadline - performance.now() <= Math.max(500, error.retryAfterMs || 0)) error.nonRetryable = true;
throw error;
} finally {
await dispatcher?.destroy();
}
}, 1, 500);
}
async function fetchEonet(days, fetchFn = globalThis.fetch, now = Date.now()) {
const url = `${EONET_API_URL}?status=open&days=${days}`;
const data = await fetchEventSourceJson('eonet', url, fetchFn);
if (!Array.isArray(data?.events)) throw new Error('EONET malformed response');
const events = [];
for (const event of data.events || []) {
const category = event.categories?.[0];
if (!category) throw new Error('EONET malformed event category');
const normalizedCategory = normalizeCategory(category.id);
if (normalizedCategory === 'earthquakes') continue;
const latestGeo = event.geometry?.[event.geometry.length - 1];
if (!latestGeo || !['Point', 'Polygon'].includes(latestGeo.type) || !Array.isArray(latestGeo.coordinates)) {
throw new Error('EONET malformed event geometry');
}
if (latestGeo.type !== 'Point') continue;
const eventDate = new Date(latestGeo.date);
const [lon, lat] = latestGeo.coordinates;
if (normalizedCategory === 'wildfires' && now - eventDate.getTime() > WILDFIRE_MAX_AGE_MS) continue;
const source = event.sources?.[0];
events.push({
id: event.id || '',
title: event.title || '',View on GitHub (pinned to e586b8b4b8)