koala73/worldmonitor · warning · RpcValidationError
country must be an ISO 3166-1 alpha-2 country code, e.g. "UA
Error message
country must be an ISO 3166-1 alpha-2 country code, e.g. "UA" for Ukraine or "US" for the United States — not a country name or a three-letter code.
What it means
This RpcValidationError is thrown by the MCP tool registry's local pre-flight guard `assertIntelHistoryCountry` in api/mcp/registry/rpc-tools.ts:509. Before the tool call reaches the HTTP handler, the registry validates the `country` filter against `^[A-Z]{2}$` and rejects anything that is not an uppercase two-letter ISO 3166-1 alpha-2 code. It exists so callers get an actionable JSON-RPC -32602 with `error.data.violations` instead of a generic -32603 internal error, and so agents do not retry a deterministic validation failure (the WORLDMONITOR-10R incident recorded 13 pointless retries in 40 seconds).
Source
Thrown at api/mcp/registry/rpc-tools.ts:609
* flattened into the generic `-32603 "Internal error: data fetch failed"` —
* strictly worse than not checking at all, since the un-guarded path at least
* relays the handler's own `-32602 Invalid params` with `error.data.violations`.
* It also keeps the `<label> HTTP 400` message shape dispatch's client-4xx
* severity downgrade matches on, so a caller-input rejection reports at
* `warning` rather than `error`.
*
* Why it matters: an agent reads -32603 as transient and retries.
* WORLDMONITOR-10R recorded 13 `search_intel_history` calls from one IP inside
* 40 seconds (2026-08-27T01:43:19Z → 01:43:58Z) against a deterministic
* validation failure.
*
* The sibling scope guard in `get_intel_timeline` (unscoped read) gets the same
* treatment in #7182 — deliberately left alone here so the two changes do not
* collide on one block.
*/
function assertIntelHistoryCountry(label: string, country: string): void {
if (country && !INTEL_HISTORY_COUNTRY_PATTERN.test(country)) {
throw new RpcValidationError(label, [{
field: 'country',
description: 'must be an ISO 3166-1 alpha-2 country code, e.g. "UA" for Ukraine or "US" for the United States — not a country name or a three-letter code.',
}]);
}
}
function procurementPageSize(value: unknown): number {
return Number.isInteger(value) && (value as number) > 0
? Math.min(PROCUREMENT_TOOL_MAX_PAGE_SIZE, value as number)
: PROCUREMENT_TOOL_DEFAULT_PAGE_SIZE;
}
/**
* The MCP tool preserves the canonical relevance-filter semantics:
* malformed/non-positive values disable the filter; values above 100 are
* deliberately passed through so the route remains the sole authority that
* clamps its documented upper bound.
*/View on GitHub (pinned to 9361220cc0)
Solutions
- Set `country` to an uppercase ISO 3166-1 alpha-2 code, e.g. "UA" or "US"
- If the caller only has a country name, map it to its alpha-2 code before calling the tool
- Omit `country` entirely when no country filter is wanted (it is optional; only the shape is validated)
Example fix
// before
tools.search_intel_history({ country: 'Ukraine' })
// after
tools.search_intel_history({ country: 'UA' }) Defensive patterns
Strategy: validation
Validate before calling
const ISO_ALPHA2 = /^[A-Z]{2}$/;
const country = 'UA'; // candidate
if (country && !ISO_ALPHA2.test(country)) {
// map from a name or alpha-3 code before calling the tool
throw new Error(`Invalid country filter: ${country}`);
}
await tools.search_intel_history({ country }); Type guard
function isIsoAlpha2Country(v: unknown): v is string {
return typeof v === 'string' && /^[A-Z]{2}$/.test(v);
} Try / catch
try {
await callTool();
} catch (e) {
if (e?.code === -32602 && e?.data?.violations?.some((v) => v.field === 'country')) {
// fix the country param; do NOT retry unchanged
} else throw e;
} Prevention
- Normalize country inputs to ISO 3166-1 alpha-2 uppercase before any MCP intel-history call
- Treat -32602 violations as deterministic: never retry the same params
When it happens
Trigger: Calling an MCP intel-history tool (e.g. `search_intel_history` or `get_intel_timeline`) with `country` set to a country name ("Ukraine"), a lowercase code ("ua"), a three-letter code ("UKR"), or any non-two-uppercase-letter string.
Common situations: LLM agents passing natural-language country names from user prompts; callers using ISO 3166-1 alpha-3 or numeric codes; passing lowercase codes; copying country labels from UI datasets instead of code tables.
Related errors
- must be an ISO 3166-1 alpha-2 country code.
- Could not resolve ${JSON.stringify(echoCountryInput(raw))} t
- get_intel_timeline requires at least one of domain ("conflic
- fromIso2 and toIso2 must be valid 2-letter ISO country codes
- iso2 must be a 2-letter uppercase ISO country code
AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-08-27).
Data as JSON: /api/errors/1ef436d39b53978a.
Report an issue: GitHub.