koala73/worldmonitor · error · Error
CF Radar DDoS API error: protocol=${protocolResp.status} vec
Error message
CF Radar DDoS API error: protocol=${protocolResp.status} vector=${vectorResp.status} What it means
fetchDdosData fetches the Cloudflare Radar layer3 attacks summary endpoints for protocol and vector in parallel (plus the optional target-locations fetch) and throws this combined error when either summary response is non-2xx. Unlike [145], this one is treated as fatal: without protocol/vector summaries the DDoS record cannot be written at all. The message embeds both status codes so you can tell at a glance which of the two upstream calls failed.
Solutions
- Read protocol=NNN vector=NNN in the message: the non-zero/non-200 side is the failing endpoint; curl it with the same headers to reproduce.
- 401/403: regenerate the Cloudflare token with Radar read permissions and update the seed env; 429: add backoff/spacing between seed runs; 5xx: retry later.
- Add one retry with short backoff around the two fetches to ride out transient 5xx/429.
- Confirm the responses are also valid JSON afterwards — a 200 with an HTML error page would instead fail later in requireRadarResult.
Example fix
null
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight both endpoints cheaply:
for (const path of ['radar/attacks/layer3/summary/protocol', 'radar/attacks/layer3/summary/vector']) {
const r = await fetch(`${CF_RADAR_BASE}/${path}?dateRange=7d`, { headers });
if (!r.ok) throw new Error(`precheck ${path}: ${r.status}`);
} Try / catch
try {
const ddos = await fetchDdosData(headers);
} catch (err) {
const m = err.message.match(/protocol=(\d+) vector=(\d+)/);
if (m && (m[1] === '429' || +m[1] >= 500 || m[2] === '429' || +m[2] >= 500)) {
await sleep(backoff); // then retry once before giving up
} else throw err;
} Prevention
- Keep the Radar token valid and scoped; rotate before expiry and test with a curl precheck.
- Space seed runs to respect Radar rate limits; add exponential backoff on 429/5xx.
- Log which side (protocol vs vector) failed using the status pair in the message.
- Check Cloudflare status page before long seed batches during incidents.
When it happens
Trigger: Either `GET /radar/attacks/layer3/summary/protocol?dateRange=7d` or `GET /radar/attacks/layer3/summary/vector?dateRange=7d` returns non-2xx (401/403 bad token, 429 rate limit, 5xx outage) after the 15s timeout window, triggering `if (!protocolResp.ok || !vectorResp.ok) throw`.
Common situations: Expired Cloudflare API token in the seed environment; Radar API partial outage affecting one summary route; running seeds frequently enough to trip Radar rate limits; token scoped to the wrong account/plan.
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 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/c523df9c0d1b2fa4.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-internet-outages.mjs:237
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(),
]);
if (!protocolResp.ok || !vectorResp.ok) {
throw new Error(`CF Radar DDoS API error: protocol=${protocolResp.status} vector=${vectorResp.status}`);
}
const [protocolResult, vectorResult] = await Promise.all([
protocolResp.json().then((data) => requireRadarResult(data, 'DDoS protocol')),
vectorResp.json().then((data) => requireRadarResult(data, 'DDoS vector')),
]);
function toEntries(summary) {
return Object.entries(summary).map(([label, pct]) => ({ label, percentage: parseFloat(pct) || 0 }))
.sort((a, b) => b.percentage - a.percentage);
}
const topTargetLocations = targetSlice.items.map((item) => {
const code = item.clientCountryAlpha2 || '';
const coords = COUNTRY_COORDS[code] || null;
return {
countryCode: code,
countryName: item.clientCountryName || code,View on GitHub (pinned to 7d06c8633d)