koala73/worldmonitor · error · ValidationError
countryCode must be a 2-letter ISO 3166-1 alpha-2 code
Error message
countryCode must be a 2-letter ISO 3166-1 alpha-2 code
What it means
getFiveFactorScorecard normalizes req.countryCode (trim + uppercase) and requires it to match /^[A-Z]{2}$/ before reading the scorecard snapshot. Anything else (empty, full country names, 3-letter codes, digits) throws this ValidationError on field 'countryCode'.
Source
Thrown at server/worldmonitor/scorecard/v1/get-five-factor-scorecard.ts:21
GetFiveFactorScorecardResponse,
ScorecardServiceHandler,
ServerContext,
} from '../../../../src/generated/server/worldmonitor/scorecard/v1/service_server';
import { ValidationError } from '../../../../src/generated/server/worldmonitor/scorecard/v1/service_server';
// @ts-expect-error — JS module, no declaration file
import { captureSilentError } from '../../../../api/_sentry-edge.js';
import { markNoStoreFallbackResponse } from '../../../_shared/response-headers';
import { asFiveFactorSnapshot, readFiveFactorSnapshot, type ScorecardSnapshotReader } from './_read-snapshot';
import { toPublicCountryScorecard } from './_response';
export async function getFiveFactorScorecardWithReader(
ctx: ServerContext,
req: GetFiveFactorScorecardRequest,
reader: ScorecardSnapshotReader,
): Promise<GetFiveFactorScorecardResponse> {
const countryCode = String(req.countryCode || '').trim().toUpperCase();
if (!/^[A-Z]{2}$/.test(countryCode)) {
throw new ValidationError([{ field: 'countryCode', description: 'countryCode must be a 2-letter ISO 3166-1 alpha-2 code' }]);
}
let snapshotValue: unknown;
try {
snapshotValue = await reader([countryCode]);
} catch (error) {
console.warn('[scorecard] snapshot read failed operation=get-five-factor-scorecard', error instanceof Error ? error.message : 'unknown');
void captureSilentError(error, { tags: { route: 'scorecard/get-five-factor-scorecard', step: 'snapshot-read' } });
return markNoStoreFallbackResponse(ctx.request, {
unavailable: true,
unavailableReason: 'scorecard-snapshot-unavailable',
});
}
const snapshot = asFiveFactorSnapshot(snapshotValue);
const record = snapshot?.countries[countryCode];
if (!snapshot || !record) {
return markNoStoreFallbackResponse(ctx.request, {
unavailable: true,
unavailableReason: snapshot ? 'country-unavailable' : 'scorecard-snapshot-unavailable',View on GitHub (pinned to 9361220cc0)
Solutions
- Send a 2-letter ISO 3166-1 alpha-2 code (e.g. 'DE', 'JP')
- Trim and uppercase the input client-side before sending (server uppercases too, so 'de' works)
- Validate with /^[A-Z]{2}$/ before the call
- Map ISO-3 or country names to alpha-2 in the client before invoking
Example fix
// before
await getFiveFactorScorecard({ countryCode: 'USA' })
// after
const code = String(raw).trim().toUpperCase();
if (!/^[A-Z]{2}$/.test(code)) throw new Error('countryCode must be alpha-2');
await getFiveFactorScorecard({ countryCode: code }) Defensive patterns
Strategy: validation
Validate before calling
function normalizeCountryCode(v) {
const code = String(v || '').trim().toUpperCase();
return /^[A-Z]{2}$/.test(code) ? code : null;
} Type guard
function isIso2(v) {
return typeof v === 'string' && /^[A-Z]{2}$/.test(v);
} Try / catch
try {
return await getFiveFactorScorecard({ countryCode });
} catch (e) {
if (e.name === 'ValidationError' && e.field === 'countryCode') {
return null; // or re-prompt with e.description
}
throw e;
} Prevention
- Store country codes as alpha-2 everywhere in the client
- Constrain inputs with a 2-letter pattern or country picker
- Normalize (trim + uppercase) at the boundary
- Map alpha-3/names to alpha-2 before calling
When it happens
Trigger: Calling the five-factor scorecard endpoint with countryCode missing, empty string, 'USA', 'us' pre-normalization edge cases won't pass since it uppercases but length still matters, numeric input, or names like 'Germany'.
Common situations: Clients storing ISO-3 codes or display names; form inputs not constrained to 2 letters; query params arriving as numbers; regional codes like 'EU' that pass the regex but are not real countries (those pass validation and fail later at snapshot read).
Related errors
- iso2 must be a 2-letter uppercase ISO country code
- fromIso2 and toIso2 must be valid 2-letter ISO country codes
- iso2 must be a 2-letter uppercase ISO country code
- iso2 must be a 2-letter uppercase ISO country code
- country must be an ISO 3166-1 alpha-2 country code, e.g. "UA
AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-09-01).
Data as JSON: /api/errors/bc528c676360f41b.
Report an issue: GitHub.