{"record":{"id":"7f0efdc5bf06a59d","repo":"koala73/worldmonitor","slug":"gdacs-search-page-incomplete-vo","errorCode":null,"errorMessage":"GDACS SEARCH page incomplete (VO)","messagePattern":"GDACS SEARCH page incomplete \\(VO\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/seed-natural-events.mjs","lineNumber":381,"sourceCode":"\nasync function fetchGdacsType(eventtype, fetchFn, now) {\n  // MAP requires one type, but its VO route returns 404. SEARCH supplies\n  // recent volcano events; their iscurrent field determines closure below.\n  const url = new URL(eventtype === 'VO' ? GDACS_API.replace(/MAP$/, 'SEARCH') : GDACS_API);\n  if (eventtype === 'VO') {\n    url.search = new URLSearchParams({\n      eventlist: 'VO',\n      fromDate: new Date(now - DAYS * 86_400_000).toISOString().slice(0, 10),\n      toDate: new Date(now).toISOString().slice(0, 10),\n      pageSize: '100', pageNumber: '1',\n    }).toString();\n  } else url.searchParams.set('eventtype', eventtype);\n  const data = await fetchEventSourceJson(`gdacs:${eventtype}`, url.toString(), fetchFn);\n  if (!Array.isArray(data?.features) || data.features.some(feature =>\n    !['Point', 'Polygon', 'MultiPolygon', 'LineString', 'MultiLineString'].includes(feature?.geometry?.type))) {\n    throw new Error(`GDACS malformed response (${eventtype})`);\n  }\n  if (eventtype === 'VO' && data.features.length >= 100) throw new Error('GDACS SEARCH page incomplete (VO)');\n  // MAP also carries impact polygons and forecast tracks. They are not event\n  // records; retaining them would inflate snapshots without adding coverage.\n  const points = data.features.filter(feature => feature.geometry.type === 'Point');\n  if (!validGdacsFeatures(points, eventtype)) throw new Error(`GDACS malformed response (${eventtype})`);\n  return points;\n}\n\nconst GDACS_ALERT_RANK = { Red: 0, Orange: 1 };\n\nexport async function fetchGdacs(fetchFn = globalThis.fetch, { previousSources = null, now = Date.now() } = {}) {\n  const types = Object.keys(GDACS_TO_CATEGORY);\n  const settled = await Promise.allSettled(types.map((eventtype) => fetchGdacsType(eventtype, fetchFn, now)));\n  const failedTypes = [];\n  const features = [];\n  const snapshots = {};\n  settled.forEach((result, i) => {\n    const type = types[i];\n    const source = `gdacs:${type}`;","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/koala73/worldmonitor/blob/e586b8b4b80f595aa7ece295eec10d76f2921240/scripts/seed-natural-events.mjs#L363-L399","documentation":"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.","triggerScenarios":"eventtype === 'VO' and data.features.length >= 100 — exactly pageSize, meaning at least one more page of volcano events likely exists.","commonSituations":"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.","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."],"exampleFix":"// before\nif (eventtype === 'VO' && data.features.length >= 100) throw new Error('GDACS SEARCH page incomplete (VO)');\n// after\nlet features = data.features;\nlet page = 1;\nwhile (eventtype === 'VO' && features.length >= 100 * page) {\n  const next = await fetchEventSourceJson(`gdacs:${eventtype}`, urlWithPage(++page), fetchFn);\n  features = features.concat(next.features);\n}","handlingStrategy":"try-catch","validationCode":"const count = (data?.features || []).length; if (count >= 100) console.warn('GDACS VO page full; pagination required for a complete snapshot');","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["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"],"tags":["api","pagination","gdacs","volcano"],"backgroundTag":"unexpected-response-shape","analyzedSha":"e586b8b4b80f595aa7ece295eec10d76f2921240","analyzedAt":"2026-09-22T01:50:49.965Z","contentChangedAt":"2026-09-22T01:50:49.965Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}