koala73/worldmonitor · error · Error

A valid country code is required for a resilience score gene

Error message

A valid country code is required for a resilience score generation

What it means

Thrown by ensureResilienceScoreGenerationCached in server/worldmonitor/resilience/v1/_shared.ts when the country-code argument cannot be normalized into a 2-letter ISO 3166-1 alpha-2 code. normalizeCountryCode() trims, uppercases, and requires /^[A-Z]{2}$/; anything else (empty, null-ish, 'usa', 'us ', 'U1') yields '' and triggers this error. The generation path is strict because the cache key and trace sidecar are keyed off the normalized code.

Source

Thrown at server/worldmonitor/resilience/v1/_shared.ts:1290

        imputationShare: 0,
        dataVersion: '',
        pillars: [],
        schemaVersion: '1.0',
        // Plan §U3: missing-cache fallback → not headline-eligible. A
        // country without a successful score build can't make the
        // PR 6 coverage gate either, so the conservative default is
        // false even during the PR-2 "true-by-default" window.
        headlineEligible: false,
      };
}

export async function ensureResilienceScoreGenerationCached(
  countryCode: string,
  reader?: ResilienceSeedReader,
): Promise<{ score: GetResilienceScoreResponse; trace: ResilienceScoreTraceSidecar }> {
  const normalizedCountryCode = normalizeCountryCode(countryCode);
  if (!normalizedCountryCode) {
    throw new Error('A valid country code is required for a resilience score generation');
  }

  await ensureResilienceScoreCached(normalizedCountryCode, reader);
  const cached = await getCachedJson(scoreCacheKey(normalizedCountryCode)) as CachedScorePayload | null;
  if (cached?._traceGenerationId && cached._traceCacheIdentity) {
    const trace = await getCachedJson(
      scoreTraceCacheKey(normalizedCountryCode, cached._traceGenerationId),
    );
    if (isMatchingTraceSidecar(trace, normalizedCountryCode, cached)) {
      return {
        score: await toPublicCachedScore(normalizedCountryCode, cached),
        trace,
      };
    }
  }

  // Missing, evicted, or malformed trace references invalidate the complete
  // generation. Rebuild score and trace together; never attach a fresh trace to

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Validate and normalize the input first: trim, uppercase, and check /^[A-Z]{2}$/ before calling.
  2. If you have a country name or ISO-3 code, map it to ISO-2 (lookup table) before invoking.
  3. Ensure callers don't pass undefined when the field is optional — default to a valid code or skip the call.
  4. For API entry points, reject invalid countryCode with a ValidationError at the request boundary, mirroring get-resilience-indicators.ts.

Example fix

// before
await ensureResilienceScoreGenerationCached(userInput.country); // may be 'Brazil' or ''
// after
const iso2 = String(userInput.country ?? '').trim().toUpperCase();
if (!/^[A-Z]{2}$/.test(iso2)) throw new ValidationError([{ field: 'countryCode', description: 'countryCode must be a 2-letter ISO 3166-1 alpha-2 code' }]);
await ensureResilienceScoreGenerationCached(iso2);
Defensive patterns

Strategy: validation

Validate before calling

function toIso2(v) { const s = String(v ?? '').trim().toUpperCase(); return /^[A-Z]{2}$/.test(s) ? s : null; }
const cc = toIso2(input.country);
if (!cc) throw new Error('countryCode must be a 2-letter ISO 3166-1 alpha-2 code');

Type guard

function isIso2(v: unknown): v is string {
  return typeof v === 'string' && /^[A-Z]{2}$/.test(v.trim().toUpperCase());
}

Try / catch

try {
  await ensureResilienceScoreGenerationCached(cc);
} catch (err) {
  if (err.message.includes('A valid country code is required')) {
    return badRequest({ field: 'countryCode', description: 'must be ISO-2' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling ensureResilienceScoreGenerationCached('') , with undefined/null, with a 3-letter code like 'USA', with lowercase-only garbage that fails normalization, or with codes containing whitespace/numbers/dashes. Unlike ensureResilienceScoreCached (which returns a blank score), the *generation* wrapper throws.

Common situations: 1) A caller passes an unvalidated user-supplied country string (e.g., from a URL query param or country name like 'Brazil'); 2) an upstream seed/manifest produces an empty code; 3) code refactors pass full country names or ISO-3 codes where ISO-2 is required.

Related errors


AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-09-01). Data as JSON: /api/errors/d5796f00babf79bb. Report an issue: GitHub.