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

  1. Read the joined `detail` messages to identify the root cause per status (network vs HTTP status vs payload), then address that specific failure first
  2. Check ECCC service status / retry after a short backoff if the cause is a transient outage or 5xx
  3. Verify network egress, proxy settings, and DNS in the environment running the script (CI runners often lack outbound access)
  4. 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

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


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)