koala73/worldmonitor · warning · RpcValidationError

Could not resolve ${JSON.stringify(echoCountryInput(raw))} t

Error message

Could not resolve ${JSON.stringify(echoCountryInput(raw))} to a country. Pass an ISO 3166-1 alpha-2 code (e.g. "IQ"), an alpha-3 code ("IRQ"), or an English country name ("Iraq").

What it means

requireCountryCode validates MCP tool arguments against resolveCountryCode, which accepts ISO 3166-1 alpha-2, alpha-3, or English country names. If the input cannot be resolved, it throws an RpcValidationError whose description embeds the echoed (sanitized) input plus the COUNTRY_ARG_HINT, telling the caller exactly which formats are accepted.

Solutions

  1. Send a valid ISO 3166-1 alpha-2 code (e.g. "IQ") — the most reliable form
  2. Or send an alpha-3 code ("IRQ") or the exact English country name ("Iraq")
  3. Trim/canonicalize the value client-side before sending
  4. Check the error description: it echoes the offending input so you can see what actually arrived (encoding/quoting bugs show up here)

Example fix

// before
callTool('get-country-brief', { country_code: 'UK' }); // RpcValidationError
// after
callTool('get-country-brief', { country_code: 'GB' }); // resolves
Defensive patterns

Strategy: validation

Validate before calling

const ISO2 = new Set(['IQ','GB','US']); // or full ISO list
function looksLikeCountryArg(v) { return typeof v === 'string' && /^[A-Za-z]{2,3}$/.test(v.trim()) || typeof v === 'string' && v.trim().length > 3; }

Type guard

function isPlausibleCountryCode(v) { return typeof v === 'string' && v.trim().length >= 2 && v.trim().length <= 56; }

Try / catch

try { return await tool.call(params); } catch (e) { if (e instanceof RpcValidationError) return { error: 'invalid_country', hint: e.violations[0]?.description }; throw e; }

Prevention

When it happens

Trigger: Calling any MCP tool that funnels country arguments through requireCountryCode (via resolveCountryFilter/countryCode/code) with an unrecognized value: full ISO alpha-2 is expected, but things like 'uk', lowercase full names with typos, numeric codes, empty-ish strings that fail normalization, or localized names will not resolve.

Common situations: LLM tool callers emitting 'UK' style or full locale names ('United Kingdom of Great Britain...'); clients sending numeric ISO 3166-1 numeric codes; older integrations sending alpha-2 with whitespace/newlines that survive trim but fail matching.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at api/mcp/_country-args.ts:26

  // Never stringify a non-string. `String(x)` runs the value's own toString /
  // valueOf, and `{"toString":"x"}` is legal JSON a caller can send: the
  // shadowed, non-callable toString makes String() throw
  // `TypeError: Cannot convert object to primitive value`. That turns this
  // guard — whose whole job is to produce a clean 400 — into a 500. Describing
  // the type is also more useful to the caller than `[object Object]`.
  const text = typeof raw === 'string' ? raw.trim() : `<non-string ${typeof raw}>`;
  return text.length > MAX_ECHOED_COUNTRY_INPUT
    ? `${text.slice(0, MAX_ECHOED_COUNTRY_INPUT)}…`
    : text;
}

export const COUNTRY_ARG_HINT =
  'Pass an ISO 3166-1 alpha-2 code (e.g. "IQ"), an alpha-3 code ("IRQ"), or an English country name ("Iraq").';

export function requireCountryCode(raw: unknown, operation: string, field = 'country_code'): string {
  const resolved = resolveCountryCode(raw);
  if (resolved) return resolved;
  throw new RpcValidationError(operation, [{
    field,
    description: `Could not resolve ${JSON.stringify(echoCountryInput(raw))} to a country. ${COUNTRY_ARG_HINT}`,
  }]);
}

/** Omitted optional filters retain the unfiltered result; invalid entries do not. */
export function resolveCountryFilter(raw: unknown, field: string): string[] {
  if (raw == null || (typeof raw === 'string' && !raw.trim())) return [];
  const values = Array.isArray(raw) ? raw : [raw];
  return values.map((value) => requireCountryCode(value, 'country-filter', field).toLowerCase());
}

View on GitHub (pinned to 7d06c8633d)