{"record":{"id":"cfb0566c9e80f8e2","repo":"koala73/worldmonitor","slug":"cloudflare-radar-source-result-field-is-missing-or-not-an-cfb056","errorCode":null,"errorMessage":"Cloudflare Radar ${source}: result.${field} is missing or not an object","messagePattern":"Cloudflare Radar (.+?): result\\.(.+?) is missing or not an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/seed-internet-outages.mjs","lineNumber":127,"sourceCode":"    return null;\n  })();\n  if (reason) throw new Error(`Cloudflare Radar ${source}: invalid success envelope (${reason})`);\n  return data.result;\n}\n\n/** Require an array-valued result field — an absent one is a failure, not zero records. */\nfunction requireRadarArray(result, field, source) {\n  if (!Array.isArray(result[field])) {\n    throw new Error(`Cloudflare Radar ${source}: result.${field} is missing or not an array`);\n  }\n  return result[field];\n}\n\n/** Require an object-valued result field (Radar summary maps). */\nfunction requireRadarObject(result, field, source) {\n  const value = result[field];\n  if (!value || typeof value !== 'object' || Array.isArray(value)) {\n    throw new Error(`Cloudflare Radar ${source}: result.${field} is missing or not an object`);\n  }\n  return value;\n}\n\nasync function fetchOutages() {\n  const token = process.env.CLOUDFLARE_API_TOKEN;\n  if (!token) {\n    console.log('CLOUDFLARE_API_TOKEN not set — skipping');\n    process.exit(0);\n  }\n\n  const resp = await fetch(`${CF_RADAR_URL}?dateRange=28d&limit=50`, {\n    headers: {\n      Authorization: `Bearer ${token}`,\n      'User-Agent': CHROME_UA,\n    },\n    signal: AbortSignal.timeout(15_000),\n  });","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/seed-internet-outages.mjs#L109-L145","documentation":"requireRadarObject() validates that a Cloudflare Radar response result field is a plain object (e.g. Radar summary maps like traffic shares by country). It rejects null/undefined, non-objects, and arrays (arrays are not valid here even though typeof array === 'object'). The script fails hard rather than seeding empty summary data.","triggerScenarios":"Calling requireRadarObject on a summary result where the field is absent, null, an array, or a primitive — typically because the Radar endpoint changed its response shape or the request returned an error payload whose result is not the expected map.","commonSituations":"Radar summary API version changes moving/rename of map fields; error responses like {result: null, errors: [...]} from auth or quota failures being passed to the validator; accidentally pointing at an array-valued endpoint (e.g. a top-N list) instead of a map endpoint.","solutions":["Inspect the full response (including the errors array Cloudflare returns) before validating; surface result:null + errors as an API error, not a shape failure.","Verify the field name against the Radar API docs for the endpoint and API version in use; update the seed script if renamed.","Check token scopes/plan entitlement for the Radar summary endpoints being queried.","Use the correct endpoint: map-shaped summary endpoints for requireRadarObject, list-shaped ones for requireRadarArray."],"exampleFix":"// before\nconst summary = requireRadarObject(result, 'clientCountries', 'traffic summary');\n// after\nif (result === null || Array.isArray(apiJson.errors) && apiJson.errors.length) {\n  throw new Error(`Radar API error: ${JSON.stringify(apiJson.errors)}`);\n}\nconst summary = requireRadarObject(result, 'clientCountries', 'traffic summary');","handlingStrategy":"type-guard","validationCode":"function expectRadarMap(apiJson, field) {\n  const result = apiJson?.result;\n  const v = result?.[field];\n  if (!v || typeof v !== 'object' || Array.isArray(v)) {\n    throw new Error(`Radar response missing object result.${field}: ${JSON.stringify(apiJson).slice(0, 300)}`);\n  }\n  return v;\n}","typeGuard":"const hasRadarObject = (result, field) => {\n  const v = result?.[field];\n  return !!v && typeof v === 'object' && !Array.isArray(v);\n};","tryCatchPattern":"try {\n  const summary = requireRadarObject(result, 'clientCountries', 'traffic summary');\n} catch (e) {\n  if (e.message.includes('missing or not an object')) {\n    console.error('Expected a Radar summary map; got:', typeof result?.[field], JSON.stringify(result).slice(0, 500));\n    throw e;\n  }\n  throw e;\n}","preventionTips":["Match endpoint shape to validator: map endpoints for requireRadarObject, list endpoints for requireRadarArray.","Validate the Cloudflare errors array first; result:null with errors is an API failure, not a shape bug.","Keep a small fixture of a real Radar summary response in tests to detect schema drift early.","Re-verify field names against Radar docs whenever bumping the API version."],"tags":["cloudflare","api","schema-change","seeding"],"backgroundTag":"unexpected-response-shape","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}