koala73/worldmonitor · error · Error

Cloudflare Radar ${source}: result.${field} is missing or no

Error message

Cloudflare Radar ${source}: result.${field} is missing or not an object

What it means

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.

Solutions

  1. Inspect the full response (including the errors array Cloudflare returns) before validating; surface result:null + errors as an API error, not a shape failure.
  2. Verify the field name against the Radar API docs for the endpoint and API version in use; update the seed script if renamed.
  3. Check token scopes/plan entitlement for the Radar summary endpoints being queried.
  4. Use the correct endpoint: map-shaped summary endpoints for requireRadarObject, list-shaped ones for requireRadarArray.

Example fix

// before
const summary = requireRadarObject(result, 'clientCountries', 'traffic summary');
// after
if (result === null || Array.isArray(apiJson.errors) && apiJson.errors.length) {
  throw new Error(`Radar API error: ${JSON.stringify(apiJson.errors)}`);
}
const summary = requireRadarObject(result, 'clientCountries', 'traffic summary');
Defensive patterns

Strategy: type-guard

Validate before calling

function expectRadarMap(apiJson, field) {
  const result = apiJson?.result;
  const v = result?.[field];
  if (!v || typeof v !== 'object' || Array.isArray(v)) {
    throw new Error(`Radar response missing object result.${field}: ${JSON.stringify(apiJson).slice(0, 300)}`);
  }
  return v;
}

Type guard

const hasRadarObject = (result, field) => {
  const v = result?.[field];
  return !!v && typeof v === 'object' && !Array.isArray(v);
};

Try / catch

try {
  const summary = requireRadarObject(result, 'clientCountries', 'traffic summary');
} catch (e) {
  if (e.message.includes('missing or not an object')) {
    console.error('Expected a Radar summary map; got:', typeof result?.[field], JSON.stringify(result).slice(0, 500));
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/cfb0566c9e80f8e2. Report an issue: GitHub.

Appendix: source

Thrown at scripts/seed-internet-outages.mjs:127

    return null;
  })();
  if (reason) throw new Error(`Cloudflare Radar ${source}: invalid success envelope (${reason})`);
  return data.result;
}

/** Require an array-valued result field — an absent one is a failure, not zero records. */
function requireRadarArray(result, field, source) {
  if (!Array.isArray(result[field])) {
    throw new Error(`Cloudflare Radar ${source}: result.${field} is missing or not an array`);
  }
  return result[field];
}

/** Require an object-valued result field (Radar summary maps). */
function requireRadarObject(result, field, source) {
  const value = result[field];
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    throw new Error(`Cloudflare Radar ${source}: result.${field} is missing or not an object`);
  }
  return value;
}

async function fetchOutages() {
  const token = process.env.CLOUDFLARE_API_TOKEN;
  if (!token) {
    console.log('CLOUDFLARE_API_TOKEN not set — skipping');
    process.exit(0);
  }

  const resp = await fetch(`${CF_RADAR_URL}?dateRange=28d&limit=50`, {
    headers: {
      Authorization: `Bearer ${token}`,
      'User-Agent': CHROME_UA,
    },
    signal: AbortSignal.timeout(15_000),
  });

View on GitHub (pinned to 7d06c8633d)