koala73/worldmonitor · error · Error
HTTP ${resp.status}
Error message
HTTP ${resp.status} What it means
fetchOptionalTargetLocations calls the Cloudflare Radar layer3 attacks/top/locations/target endpoint (7d range) and throws `HTTP ${resp.status}` when `resp.ok` is false, i.e. any status outside 200-299. This seed marks the DDoS target-locations feed as optional/degradable, but the throw still propagates unless caught upstream, so a persistent 4xx from this endpoint blanks the DDoS map's target countries while protocol/vector record counts never move and overall health stays OK. The error string carries only the numeric status, not the body, so diagnosis usually needs the status code alone.
Solutions
- Verify the Cloudflare API token used for `headers` is valid and has Radar read scope (curl the endpoint manually with the same headers).
- Check the exact status in the message: 401/403 → fix token/permissions; 429 → back off and rerun; 5xx → retry later or during a Radar incident check status.cloudflare.com.
- Since this feed is deliberately optional, wrap the `fetchOptionalTargetLocations()` call (in fetchDdosData's Promise.all) with `.catch(() => ({ items: [], degraded: true }))` so a broken target-locations route degrades instead of killing the seed.
- Log `degraded: true` to seed-meta/health output so the blank DDoS target map is observable instead of silent.
Example fix
// before
fetchOptionalTargetLocations(),
// after
fetchOptionalTargetLocations().catch((err) => {
console.warn(`DDoS target locations degraded: ${err.message}`);
return { items: [], degraded: true };
}), Defensive patterns
Strategy: try-catch
Validate before calling
// Best-effort pre-check before the seed run:
const resp = await fetch(`${CF_RADAR_BASE}/radar/attacks/layer3/top/locations/target?dateRange=7d`, { headers: { Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}` } });
if (!resp.ok) console.warn(`target-locations precheck failed: ${resp.status}; feed will degrade`); Try / catch
try {
const targets = await fetchOptionalTargetLocations();
} catch (err) {
if (!/^HTTP \d+$/.test(err.message)) throw err; // only swallow the expected HTTP-status error
logDegraded('ddos-target-locations', err.message);
const targets = { items: [], degraded: true };
} Prevention
- Rotate and scope-check the Cloudflare token before scheduled runs; alert on 401/403 statuses.
- Treat explicitly-optional feeds as degradable: catch and mark degraded instead of failing the seed.
- Record per-feed degradation in seed-meta so a blank DDoS target map is visible in health output.
- Monitor Cloudflare status/Radar incidents for 5xx clusters.
When it happens
Trigger: Cloudflare Radar returns a non-2xx status for GET /radar/attacks/layer3/top/locations/target?dateRange=7d — e.g. expired/invalid CF API token (401/403), plan-level access restriction on the target-locations route, transient 5xx, or a rate-limit 429 — detected by the `if (!resp.ok) throw` check after a 15s-capped fetch.
Common situations: Rotated or unscoped CLOUDFLARE_API_TOKEN lacking Radar read permission; free-tier account hitting routes behind a paid plan; Radar API incident/degradation returning 500s; hitting per-minute quota during repeated seed runs.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- CF Radar DDoS API error: protocol=${protocolResp.status} vec
- CF Radar traffic anomalies API error: ${resp.status}
- EONET ${res.status}
- Redis HTTP ${resp.status}
- HTTP ${response.status}
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/69427b08a21cb20e.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-internet-outages.mjs:217
* logged, but must not withhold a confirmed protocol/vector summary. Keep this
* distinction explicit: silently promoting the optional slice to required (or
* demoting a required one) changes published coverage without changing counts.
*/
async function fetchDdosData(token) {
const headers = {
'User-Agent': CHROME_UA,
...(token ? { Authorization: `Bearer ${token}` } : {}),
};
// Reports `degraded` rather than just returning [] so the caller can stamp the
// shortfall on seed-meta. An unconfirmed empty slice riding inside a CONFIRMED
// payload is the exact conflation this issue is about: without the marker, a
// permanently 4xx target endpoint would blank the DDoS map's target countries
// forever while recordCount (protocol+vector) never moves and health stays OK.
const fetchOptionalTargetLocations = async () => {
try {
const resp = await fetch(`${CF_RADAR_BASE}/radar/attacks/layer3/top/locations/target?dateRange=7d`, { headers, signal: AbortSignal.timeout(15_000) });
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const result = requireRadarResult(await resp.json(), 'DDoS target locations');
return {
items: requireRadarArray(result, 'top_0', 'DDoS target locations')
.filter((item) => item && typeof item === 'object' && !Array.isArray(item)),
degraded: false,
};
} catch (err) {
console.warn(` CF Radar DDoS target locations unavailable (optional slice): ${err?.message || err}`);
return { items: [], degraded: true };
}
};
const [protocolResp, vectorResp, targetSlice] = await Promise.all([
fetch(`${CF_RADAR_BASE}/radar/attacks/layer3/summary/protocol?dateRange=7d`, { headers, signal: AbortSignal.timeout(15_000) }),
fetch(`${CF_RADAR_BASE}/radar/attacks/layer3/summary/vector?dateRange=7d`, { headers, signal: AbortSignal.timeout(15_000) }),
fetchOptionalTargetLocations(),
]);
View on GitHub (pinned to 7d06c8633d)