koala73/worldmonitor · critical · Error
ECCC issued and continued fetches both failed: ${detail}
Error message
ECCC issued and continued fetches both failed: ${detail} What it means
This error aggregates all failures from fetching both ECCC alert statuses (issued and continued). The script attempts each live status, collecting errors; if EVERY status fetch fails, it throws with all underlying messages joined so the caller sees why neither collection could be built. It exists to prevent publishing a silently truncated national alert set from partial data.
Solutions
- Read the joined `detail` messages to identify the root cause per status (network vs HTTP status vs payload), then address that specific failure first
- Check ECCC service status / retry after a short backoff if the cause is a transient outage or 5xx
- Verify network egress, proxy settings, and DNS in the environment running the script (CI runners often lack outbound access)
- Confirm the ECCC endpoint base URL and User-Agent configuration have not drifted from current API requirements
Example fix
// before
throw new Error(`ECCC issued and continued fetches both failed: ${detail}`);
// after
try {
return await fetchEcccStatuses();
} catch (err) {
console.error(err.message);
return { issued: [], continued: [], degraded: true };
} Defensive patterns
Strategy: fallback
Validate before calling
null
Type guard
null
Try / catch
try {
const statuses = await fetchEcccIssuedAndContinued();
} catch (err) {
if (String(err.message).startsWith('ECCC issued and continued fetches both failed')) {
// keep last-known-good alert set; do not publish an empty/truncated set
useStaleAlertSnapshot();
}
} Prevention
- Keep the previous successful alert snapshot and serve it with a staleness flag on total failure
- Schedule fetches with jitter/backoff so transient outages self-heal
- Alert/monitor when this aggregate error fires — it means the national alert feed is unavailable
- Keep the underlying per-status errors (the joined detail) in logs for triage
When it happens
Trigger: Both the issued-status and continued-status ECCC fetches throw (network failure, non-OK HTTP, invalid payloads, timeouts, byte-budget limits); `failures.length === ECCC_LIVE_STATUSES.length` triggers the aggregate throw with each error's message.
Common situations: ECCC API outage or maintenance window; corporate proxy/firewall blocking the endpoint; expired upstream TLS or DNS failure in CI; rate limiting or 5xx from the weather service; misconfigured base URL in environment.
Related errors
- HTTP ${response.status}
- relay returned ${resp.status}
- DNS ${recordType} lookup failed: HTTP ${response.status}
- callbackUrl DNS resolution failed: ${message}
- HTTP ${resp.status}
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/576e1741701ee999.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/_weather-alert-select.mjs:798
|| (page.length === 0 && statusFeatures.length < matched)) {
throw new Error('ECCC_PAGE_PROGRESS');
}
for (const feature of page) {
if (typeof feature?.id !== 'string' || !feature.id.trim()) throw new Error('ECCC_INVALID_ID');
if (seenIds.has(feature.id)) throw new Error('ECCC_DUPLICATE_ID');
seenIds.add(feature.id);
}
statusFeatures.push(...page);
} while (statusFeatures.length < matched);
features.push(...statusFeatures);
} catch (err) {
failures.push(err);
failedStatuses.push(status);
}
}
if (failures.length === ECCC_LIVE_STATUSES.length) {
const detail = failures.map((err) => err?.message || String(err)).join('; ');
throw new Error(`ECCC issued and continued fetches both failed: ${detail}`);
}
// Returns an OBJECT, not a bare array, so a partial fetch cannot be consumed
// as if it were the whole set. `issued` and `continued` are separate collections
// and each carries alerts the other does not: `continued` is where an ONGOING
// warning lives after its first issue. Returning just the surviving features
// when one status 500s publishes a silently truncated national alert set —
// and on the relay, whose purge semantics always overwrite, it DELETES every
// continued alert from the live key while health still reads OK.
return {
features,
failedStatuses,
partial: failedStatuses.length > 0,
failureDetail: failures.map((err) => err?.message || String(err)).join('; '),
};
}
/**
* Host-policy fetch: allowlist, reject redirects, timeout, byte ceiling.View on GitHub (pinned to 7d06c8633d)