koala73/worldmonitor · error
GDACS SEARCH page incomplete (VO)
Error message
GDACS SEARCH page incomplete (VO)
What it means
Thrown by fetchGdacsType when fetching volcanoes (eventtype 'VO'): GDACS SEARCH pagination returns a full page (>= 100 features, the pageSize), so there may be more pages and the snapshot would be incomplete. The seeder refuses to cap silently at one page.
Solutions
- Implement follow-up pagination: request pageNumber 2+ until a page returns fewer than pageSize features.
- Retry later if the count is borderline (e.g. exactly 100) — the active-event count may drop below the page size.
- Verify the SEARCH query filters (alert levels, active-only) are not accidentally widening the result set.
- If intentional, raise pageSize above 100 if the GDACS endpoint supports it.
Example fix
// before
if (eventtype === 'VO' && data.features.length >= 100) throw new Error('GDACS SEARCH page incomplete (VO)');
// after
let features = data.features;
let page = 1;
while (eventtype === 'VO' && features.length >= 100 * page) {
const next = await fetchEventSourceJson(`gdacs:${eventtype}`, urlWithPage(++page), fetchFn);
features = features.concat(next.features);
} Defensive patterns
Strategy: try-catch
Validate before calling
const count = (data?.features || []).length; if (count >= 100) console.warn('GDACS VO page full; pagination required for a complete snapshot'); Try / catch
try { const points = await fetchGdacsType('VO', fetchFn); } catch (e) { if (e.message.includes('GDACS SEARCH page incomplete')) { console.error('More than one page of volcano events; implement page following or retry'); } throw e; } Prevention
- Always page until results < pageSize instead of assuming one page suffices
- Monitor GDACS active-volcano counts; alert when they approach the page size
- Raise pageSize if the endpoint supports larger pages
- Keep the previous VO snapshot as a fallback when pagination is incomplete
When it happens
Trigger: eventtype === 'VO' and data.features.length >= 100 — exactly pageSize, meaning at least one more page of volcano events likely exists.
Common situations: An unusually active volcanic period exceeding 100 concurrent GDACS volcano events; a GDACS query change returning duplicate/all-history entries inflating the count past one page.
Related errors
- ECCC_AGGREGATE_TOO_LARGE
- ECCC_PAGE_LIMIT
- GDACS malformed response
- GDACS unavailable
- Sentry issues pagination exceeded
AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-22).
Data as JSON: /api/errors/7f0efdc5bf06a59d.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-natural-events.mjs:381
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) => {
const type = types[i];
const source = `gdacs:${type}`;View on GitHub (pinned to e586b8b4b8)