koala73/worldmonitor · error · Error
CF Radar traffic anomalies API error: ${resp.status}
Error message
CF Radar traffic anomalies API error: ${resp.status} What it means
fetchTrafficAnomalies calls the Cloudflare Radar traffic_anomalies endpoint (dateRange=7d, limit=100) and throws `CF Radar traffic anomalies API error: ${resp.status}` when the response status is not ok. This is the fatal, non-degradable guard for the traffic-anomalies portion of the internet-outages seed; any non-2xx aborts that dataset. The status code is the only diagnostic detail preserved.
Solutions
- Curl the exact URL with the seed's headers to see the status and response body directly.
- 401/403: fix token validity/scope; 429: space out runs or add exponential backoff; 5xx: retry and check Cloudflare status.
- Wrap the fetch in a small retry helper (2-3 attempts, backoff) for transient statuses before failing the seed.
- If the endpoint is optional to your use case, downgrade to a degraded result like fetchOptionalTargetLocations does instead of throwing.
Example fix
// before
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(15_000) });
if (!resp.ok) throw new Error(`CF Radar traffic anomalies API error: ${resp.status}`);
// after
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(15_000) });
if (!resp.ok) {
if (resp.status === 429 || resp.status >= 500) return retryAfterBackoff(url, headers);
throw new Error(`CF Radar traffic anomalies API error: ${resp.status} ${await resp.text().catch(() => '')}`);
} Defensive patterns
Strategy: retry
Validate before calling
const pre = await fetch(`${CF_RADAR_BASE}/radar/traffic_anomalies?dateRange=7d&limit=100`, { headers });
console.log(`traffic_anomalies reachable: ${pre.ok} (${pre.status})`); Try / catch
try {
const anomalies = await fetchTrafficAnomalies(headers);
} catch (err) {
const status = Number(err.message.match(/(\d+)$/)?.[1]);
if (status === 429 || status >= 500) await retryWithBackoff(() => fetchTrafficAnomalies(headers), 3);
else throw err;
} Prevention
- Validate the API token's Radar scope in CI before running seeds.
- Apply exponential backoff for 429/5xx instead of immediate failure.
- Capture the response body text in the error for faster diagnosis.
- Monitor per-hour request counts against Radar quota.
When it happens
Trigger: GET /radar/traffic_anomalies?dateRange=7d&limit=100 returns 401/403 (invalid or under-scoped API token), 429 (rate limit), or 5xx (Radar outage), caught by `if (!resp.ok) throw`.
Common situations: Token without the traffic-anomalies (Radar) scope; quota exhaustion from repeated 15s-timeout seed runs; Cloudflare-side incident returning 500/503.
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
- HTTP ${resp.status}
- CF Radar DDoS API error: protocol=${protocolResp.status} vec
- 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/b75ffe0432596cc4.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-internet-outages.mjs:284
dateRangeStart: meta?.dateRange?.[0]?.startTime || '',
dateRangeEnd: meta?.dateRange?.[0]?.endTime || '',
topTargetLocations,
_targetLocationsDegraded: targetSlice.degraded,
};
}
async function fetchTrafficAnomalies(token) {
const headers = {
'User-Agent': CHROME_UA,
...(token ? { Authorization: `Bearer ${token}` } : {}),
};
const resp = await fetch(`${CF_RADAR_BASE}/radar/traffic_anomalies?dateRange=7d&limit=100`, {
headers,
signal: AbortSignal.timeout(15_000),
});
if (!resp.ok) throw new Error(`CF Radar traffic anomalies API error: ${resp.status}`);
const result = requireRadarResult(await resp.json(), 'traffic anomalies');
const raw = requireRadarArray(result, 'trafficAnomalies', 'traffic anomalies');
const anomalies = raw.map((item) => {
const coords = COUNTRY_COORDS[item.locationDetails?.code] || null;
return {
uuid: item.uuid || '',
type: item.type || '',
status: item.status || '',
startDate: toEpochMs(item.startDate),
endDate: toEpochMs(item.endDate),
asn: item.asnDetails?.asn ? String(item.asnDetails.asn) : '',
asnName: item.asnDetails?.name || '',
locationCode: item.locationDetails?.code || '',
locationName: item.locationDetails?.name || '',
latitude: coords ? coords[0] : 0,
longitude: coords ? coords[1] : 0,View on GitHub (pinned to 7d06c8633d)