{"record":{"id":"51c0b75a4521efa8","repo":"koala73/worldmonitor","slug":"iso2-must-be-a-2-letter-uppercase-iso-country-code-51c0b7","errorCode":null,"errorMessage":"iso2 must be a 2-letter uppercase ISO country code","messagePattern":"iso2 must be a 2-letter uppercase ISO country code","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/supply-chain/v1/get-country-vulnerabilities.ts","lineNumber":30,"sourceCode":"  VULNERABILITY_COHORT_KEY,\n  countryVulnerabilityShardKey,\n  enforceCommodityRedistributionPolicy,\n  hasCurrentRedistributionPolicy,\n  isMatchingShard,\n  locateEntityShard,\n  mapCommodityVulnerability,\n  type RawVulnerabilityCohort,\n  type RawCountryShard,\n  stringValue,\n} from './_vulnerability-projection';\n\nexport async function getCountryVulnerabilities(\n  ctx: ServerContext,\n  req: GetCountryVulnerabilitiesRequest,\n): Promise<GetCountryVulnerabilitiesResponse> {\n  const iso2 = (req.iso2 || '').trim().toUpperCase();\n  if (!/^[A-Z]{2}$/.test(iso2)) {\n    throw new ValidationError([{ field: 'iso2', description: 'iso2 must be a 2-letter uppercase ISO country code' }]);\n  }\n\n  const persistedPayload = await getCachedJson(VULNERABILITY_COHORT_KEY, true)\n    .catch(() => null) as RawVulnerabilityCohort | null;\n  const payload = hasCurrentRedistributionPolicy(persistedPayload) ? persistedPayload : null;\n  let country = payload?.countries?.[iso2];\n  let shardUnavailable = false;\n  if (payload && !payload.countries) {\n    const located = locateEntityShard(payload, payload.countryIds, iso2, countryVulnerabilityShardKey);\n    if (located.status === 'unavailable') {\n      shardUnavailable = true;\n    } else if (located.status === 'read') {\n      const shard = await getCachedJson(located.key, true)\n        .catch(() => null) as RawCountryShard | null;\n      if (isMatchingShard(payload, shard) && shard?.country?.iso2 === iso2) country = shard.country;\n      else shardUnavailable = true;\n    }\n  }","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/server/worldmonitor/supply-chain/v1/get-country-vulnerabilities.ts#L12-L48","documentation":"getCountryVulnerabilities normalizes req.iso2 (trim + uppercase) and requires /^[A-Z]{2}$/ before looking up the country in the vulnerability cohort payload. Invalid values throw this ValidationError on field 'iso2'.","triggerScenarios":"Passing empty/missing iso2, ISO-3 codes ('BRA'), full country names, numeric country codes, or lowercase codes (lowercase is accepted because of toUpperCase, but wrong length is not).","commonSituations":"Clients using UN M49 numeric codes or FIPS codes; form fields allowing free text; mixing alpha-2 and alpha-3 datasets.","solutions":["Send an uppercase 2-letter ISO 3166-1 alpha-2 code ('BR', not 'BRA')","Trim and uppercase client-side (server does this anyway)","Validate with /^[A-Z]{2}$/ before the call","Convert alpha-3/name inputs to alpha-2 via a lookup map before invoking"],"exampleFix":"// before\nawait getCountryVulnerabilities({ iso2: 'BRA' })\n// after\nconst iso2 = String(raw).trim().toUpperCase();\nif (!/^[A-Z]{2}$/.test(iso2)) throw new Error('iso2 must be alpha-2');\nawait getCountryVulnerabilities({ iso2: iso2 })","handlingStrategy":"validation","validationCode":"function normalizeIso2(v) {\n  const code = String(v || '').trim().toUpperCase();\n  return /^[A-Z]{2}$/.test(code) ? code : null;\n}","typeGuard":"function isIso2(v) {\n  return typeof v === 'string' && /^[A-Z]{2}$/.test(v);\n}","tryCatchPattern":"try {\n  return await getCountryVulnerabilities({ iso2 });\n} catch (e) {\n  if (e.name === 'ValidationError' && e.field === 'iso2') {\n    return null;\n  }\n  throw e;\n}","preventionTips":["Use alpha-2 codes consistently across datasets","Validate iso2 format at form level","Normalize case/whitespace before sending","Maintain an alpha-3 -> alpha-2 mapping for legacy data"],"tags":["validation","iso-3166","input-validation"],"backgroundTag":"invalid-request-parameter","analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-09-01T10:32:37.851Z","contentChangedAt":"2026-09-01T10:32:37.851Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}