koala73/worldmonitor · error · RpcValidationError

get-food-stocks validation: country_code is required and…

Error message

get-food-stocks validation: country_code is required and must be a 2-letter ISO 3166-1 alpha-2 code or WORLD

What it means

The get-food-stocks MCP tool validates its required `country_code` parameter against `/^(?:[A-Z]{2}|WORLD|WLD|_WORLD)$/` after trimming and uppercasing. If the value is missing, not a string, or not a 2-letter ISO 3166-1 alpha-2 code or a WORLD sentinel, it throws an RpcValidationError describing the expected format. This fail-fast guard keeps malformed country codes from reaching the downstream food-stocks API query.

Solutions

  1. Pass a valid 2-letter ISO 3166-1 alpha-2 code (e.g. "DE", "US") or "WORLD" for the country_code parameter.
  2. Trim and uppercase the value before calling: "de" works because the tool normalizes, but " de " with internal whitespace does not.
  3. If using a 3-letter alpha-3 code, convert it to alpha-2 first (e.g. "DEU" -> "DE").
  4. If using a country name, resolve it to its alpha-2 code with an ISO lookup table before calling.

Example fix

// before
await callTool('get-food-stocks', { country_code: 'Germany' });
// after
await callTool('get-food-stocks', { country_code: 'DE' }); // or 'WORLD'
Defensive patterns

Strategy: validation

Validate before calling

const code = typeof cc === 'string' ? cc.trim().toUpperCase() : '';
if (!/^(?:[A-Z]{2}|WORLD|WLD|_WORLD)$/.test(code)) throw new Error(`invalid country_code: ${cc}`);

Type guard

const isCountryCode = (v) => typeof v === 'string' && /^(?:[A-Z]{2}|WORLD|WLD|_WORLD)$/.test(v.trim().toUpperCase());

Try / catch

try { await callTool('get-food-stocks', { country_code }) } catch (e) { if (String(e.message).includes('country_code is required')) { /* fall back to WORLD or fix the code */ } else throw e; }

Prevention

When it happens

Trigger: Calling the get-food-stocks tool with country_code omitted, with a full country name like "Germany", with a 3-letter code like "DEU", with lowercase-with-spaces strings that trim to something non-matching (e.g. "D E"), or with a non-string value such as a number.

Common situations: LLM clients passing country names instead of ISO codes; clients using ISO 3166-1 alpha-3 codes; callers forgetting the parameter entirely; case sensitivity confusion resolved only if the caller pre-uppercases.

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@e586b8b4b8 (2026-09-22). Data as JSON: /api/errors/ce6b777ca0ede01b. Report an issue: GitHub.

Appendix: source

Thrown at api/mcp/registry/rpc-tools.ts:1992

              unit: { type: 'string', description: 'Always "1000 MT" (thousand metric tons).' },
              source: {
                type: 'string',
                description: '"psd" = USDA. "faostat" = FAOSTAT Food Balances production and domestic-supply gap fill. FAOSTAT stock fields are placeholder 0 values when presence flags are false.',
              },
            },
          },
        },
        fetchedAt: { type: 'string' },
        unavailable: { type: 'boolean' },
        calorieWeightedStocksToUse: { type: 'number' },
      },
    },
    annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
    _coverageKeys: ['resilience:food-stocks:v1', 'seed-meta:resilience:food-stocks'],
    _execute: async (params, base, context, execution) => {
      const countryCode = typeof params.country_code === 'string' ? params.country_code.trim().toUpperCase() : '';
      if (!/^(?:[A-Z]{2}|WORLD|WLD|_WORLD)$/.test(countryCode)) {
        throw new RpcValidationError('get-food-stocks', [{
          field: 'country_code',
          description: 'country_code is required and must be a 2-letter ISO 3166-1 alpha-2 code or WORLD',
        }]);
      }
      const q = new URLSearchParams({ countryCode });
      if (params.commodity) q.set('commodity', String(params.commodity).trim());
      const qs = q.toString();
      const url = `${base}/api/resilience/v1/get-food-stocks${qs ? `?${qs}` : ''}`;
      const auth = await buildAuthHeaders(context, 'GET', url, null);
      const res = await fetchMcpDownstream(url, {
        headers: { ...auth, 'User-Agent': 'worldmonitor-mcp-edge/1.0' },
        signal: AbortSignal.timeout(8_000),
      }, execution);
      await assertToolFetchOk(res, 'get-food-stocks');
      return res.json();
    },
    _apiPaths: [
      'GET /api/resilience/v1/get-food-stocks',

View on GitHub (pinned to e586b8b4b8)