koala73/worldmonitor · error · Error
ECCC_AGGREGATE_TOO_LARGE
ECCC_AGGREGATE_TOO_LARGE
Error message
ECCC_AGGREGATE_TOO_LARGE
What it means
All ECCC alert pages share one aggregate byte budget of ECCC_MAX_AGGREGATE_BYTES = 8 MiB across the 'issued' and 'continued' collections (each single page is also capped at ECCC_MAX_BYTES = 4 MiB). The loop checks `byteBudget.remaining <= 0` before each page and throws Error('ECCC_AGGREGATE_TOO_LARGE') to prevent the combined national alert payload from growing unbounded. readResponseLimited() also throws this when streaming bytes push the shared budget negative.
Solutions
- Inspect failureDetail from the aggregate error to see whether 'issued' or 'continued' (or both) exhausted the budget; a single status failing yields a partial result, not a throw.
- If real alert volume legitimately exceeds 8 MiB, raise ECCC_MAX_AGGREGATE_BYTES in scripts/_weather-alert-select.mjs:48, keeping ECCC_MAX_BYTES per page within the deliberate 2–4 MiB ceiling.
- Check whether the ECCC API started returning extra fields or verbose geometries that bloat each feature; consider trimming or compacting the payload upstream.
- Confirm no earlier page consumed budget twice (byteBudget is decremented inside readResponseLimited per chunk; don't decrement again at call sites).
Example fix
// before const ECCC_MAX_AGGREGATE_BYTES = 8 * 1024 * 1024; // after (national alert set legitimately grew) const ECCC_MAX_AGGREGATE_BYTES = 12 * 1024 * 1024;
Defensive patterns
Strategy: try-catch
Validate before calling
// Estimate aggregate size before running the full fetch
const headIssued = await fetchFn(urlIssued + '&limit=1');
const headCont = await fetchFn(urlContinued + '&limit=1');
const matched = (await headIssued.json()).numberMatched + (await headCont.json()).numberMatched;
// ~3KB average per feature; warn if near the 8 MiB aggregate budget
if (matched * 3000 > 8 * 1024 * 1024) console.warn('ECCC aggregate byte budget at risk'); Try / catch
try {
const result = await fetchEcccAlertFeatures({ fetchFn, userAgent });
} catch (err) {
if (String(err.message).includes('ECCC_AGGREGATE_TOO_LARGE')) {
reportHealth('eccc', { ok: false, reason: 'aggregate-too-large', detail: err.message });
} else throw err;
} Prevention
- Track typical per-feature payload size; revisit ECCC_MAX_AGGREGATE_BYTES if the API adds fields.
- Keep maxBytes (per page, 4 MiB) coherent with the 8 MiB aggregate budget — raising one without the other hides failures.
- Never decrement byteBudget manually at call sites; only readResponseLimited owns it.
- Check failureDetail to identify which status exhausted the budget before tuning constants.
When it happens
Trigger: Fetching a page in scripts/_weather-alert-select.mjs:763 when the running total of all previously read pages has exhausted the 8 MiB shared budget (byteBudget.remaining <= 0), or inside readResponseLimited when cumulative streamed bytes drive byteBudget.remaining below 0.
Common situations: An unusually active severe-weather day where issued+continued alerts together exceed 8 MiB of JSON; an upstream API change that inflates each feature's payload size; a misconfigured caller raising maxBytes so individual pages no longer trip RESPONSE_TOO_LARGE but the aggregate does; a stub fetch in tests returning very large bodies for many pages.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- ECCC_PAGE_LIMIT
- Sentry issues pagination exceeded ${MAX_PAGES} pages
- Invalid scorecard bloc selection.
- invalid_cursor
- invalid_limit
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/c74d5d7c06e98cfe.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/_weather-alert-select.mjs:763
export async function fetchEcccAlertFeatures({
fetchFn = globalThis.fetch,
userAgent,
maxBytes = ECCC_MAX_BYTES,
} = {}) {
const byteBudget = { remaining: ECCC_MAX_AGGREGATE_BYTES };
let pages = 0;
const seenIds = new Set();
const features = [];
const failures = [];
const failedStatuses = [];
// Sequential paging makes the shared resource limits deterministic.
for (const [index, status] of ECCC_LIVE_STATUSES.entries()) {
const statusFeatures = [];
let matched;
try {
do {
if (pages >= ECCC_MAX_PAGES) throw new Error('ECCC_PAGE_LIMIT');
if (byteBudget.remaining <= 0) throw new Error('ECCC_AGGREGATE_TOO_LARGE');
const url = new URL(ECCC_ALERTS_URLS[index]);
url.searchParams.set('offset', String(statusFeatures.length));
pages += 1;
const data = await fetchApprovedWeatherJson(url.toString(), {
allowedHosts: [ECCC_HOST], maxBytes, fetchFn, userAgent, byteBudget,
});
const page = requireAlertFeatures(data);
if (data.type !== 'FeatureCollection'
|| !Number.isSafeInteger(data.numberMatched) || data.numberMatched < 0
|| !Number.isSafeInteger(data.numberReturned) || data.numberReturned !== page.length
|| page.length > ECCC_PAGE_SIZE) {
throw new Error('ECCC_MALFORMED_PAGE');
}
if (matched !== undefined && data.numberMatched !== matched) throw new Error('ECCC_COUNT_DRIFT');
matched = data.numberMatched;
if (statusFeatures.length + page.length > matched
|| (page.length === 0 && statusFeatures.length < matched)) {
throw new Error('ECCC_PAGE_PROGRESS');View on GitHub (pinned to 7d06c8633d)