koala73/worldmonitor · warning
Conflict seed unavailable
Error message
Conflict seed unavailable
What it means
buildSources' listConflicts seed reader calls the internal ACLED listAcledEvents handler and, if the response is flagged X-No-Cache (the handler's signal that upstream data could not be fetched and no cached fallback exists), it throws 'Conflict seed unavailable'. The map-frame embed aborts rather than rendering a stale or empty conflict layer.
Solutions
- Verify the ACLED upstream/API key is valid and within rate limits
- Warm the conflict cache (seed job) so the embed can serve cached data
- Retry the embed after the ACLED upstream recovers — the failure is per-request
- Optionally degrade the embed UI to hide the conflicts layer instead of failing the whole frame
Example fix
// before
const frame = await fetch('/api/embed/map-frame'); // 500 on conflict-seed failure
// after
const res = await fetch('/api/embed/map-frame');
if (!res.ok) renderFrameWithoutConflicts(await res.text().catch(() => '')); Defensive patterns
Strategy: fallback
Validate before calling
// before rendering the embed, confirm the conflict feed is warm
const health = await fetch('/api/health'); // check the acled/conflict standalone health key first Type guard
null
Try / catch
try { frame = await renderMapFrame(); } catch (e) { if (e.message === 'Conflict seed unavailable') return renderMapFrameWithoutConflicts(); throw e; } Prevention
- Keep ACLED credentials valid and monitor their rate-limit headroom
- Schedule cache seed/refresh jobs so a cold cache never coincides with upstream failure
- Per-layer degradation: let one data source failing not kill the whole embed
- Alert on X-No-Cache headers from the ACLED handler
When it happens
Trigger: Rendering /api/embed/map-frame while the ACLED upstream fails or is rate-limited and no cached conflict data exists, causing listAcledEvents to respond with X-No-Cache. Only affects the conflicts layer; earthquakes/natural events have separate readers.
Common situations: ACLED API key missing or expired; ACLED rate limit hit on a busy embed; upstream outage during a cold cache; Redis cache eviction leaving no seed.
Related errors
- relay returned ${resp.status}
- Physical divergence composite repeats a metal weight
- Physical divergence composite does not match its member read
- Physical divergence snapshot has an invalid envelope
- Wikidata request failed with status ${response.status}
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/a17d9113da528ca2.
Report an issue: GitHub.
Appendix: source
Thrown at api/embed/map-frame.ts:78
/** Build the generated handler context for an in-process source read. */
function handlerContext(req: Request) {
return { request: req, pathParams: {}, headers: Object.fromEntries(req.headers) };
}
function buildSources(req: Request): EmbedMapFrameSources {
const ctx = handlerContext(req);
// Each upstream is called with ITS OWN defaults — zeros mean "handler
// default" across these generated requests. Forwarding a caller-supplied
// window or page size here would hand a stolen credential the knobs this
// endpoint exists to remove.
return {
listConflicts: async () => {
// Isolate this source's fallback signal from the other parallel readers.
const conflictReq = new Request(req);
const response = await listAcledEvents(handlerContext(conflictReq), { start: 0, end: 0, pageSize: 0, cursor: '', country: '' });
if (drainResponseHeaders(conflictReq)?.['X-No-Cache']) {
markNoCacheResponse(req);
throw new Error('Conflict seed unavailable');
}
return response.events;
},
listEarthquakes: async () =>
(await listEarthquakes(ctx, { minMagnitude: 0, start: 0, end: 0, pageSize: 0, cursor: '' })).earthquakes,
listNaturalEvents: async () => (await listNaturalEvents(ctx, { days: 30 })).events,
listProtests: async () =>
(await listUnrestEvents(ctx, {
country: '',
start: 0,
end: 0,
pageSize: 0,
cursor: '',
// The handler reads neither the bbox nor minSeverity; these are here
// only to satisfy the generated request type. Kept at their zero values
// so that if it ever starts reading them, the embed asks for no filter
// rather than silently inheriting one.
minSeverity: 'SEVERITY_LEVEL_UNSPECIFIED',View on GitHub (pinned to 7d06c8633d)