koala73/worldmonitor · error
get_intel_timeline requires at least one of domain…
Error message
get_intel_timeline requires at least one of domain ("conflict", "military", or "energy") or country (ISO 3166-1 alpha-2) — those are the two indexed scopes on the history store. What it means
Plain Error thrown in get_intel_timeline's _execute (api/mcp/registry/rpc-tools.ts:2256): the client-side scope pre-check found neither a usable domain ('conflict', 'military', or 'energy') nor a country (ISO 3166-1 alpha-2) in the params — after trimming, both were empty strings. The handler enforces this server-side with a 400 anyway (those are the only two indexed scopes on the history store); the MCP layer checks first to convert the opaque downstream failure into an actionable message and save the round-trip. The handler remains the enforcing authority.
Solutions
- Pass at least one of domain ('conflict' | 'military' | 'energy') or country (ISO 3166-1 alpha-2, e.g. 'UA') in the call params
- Trim and validate user-supplied domain/country before invoking the tool — mirror the exact enum and alpha-2 rules
- If you intended a global timeline, note it is unsupported by design: the history store only has those two indexes
Example fix
// before
const res = await client.callTool('get_intel_timeline', { limit: 50 });
// throws: requires at least one of domain ... or country ...
// after
const res = await client.callTool('get_intel_timeline', { domain: 'conflict', limit: 50 });
// or
const res = await client.callTool('get_intel_timeline', { country: 'UA', limit: 50 }); Defensive patterns
Strategy: validation
Validate before calling
// Mirror the indexed scopes before calling
const DOMAINS = new Set(['conflict', 'military', 'energy']);
const domain = typeof p.domain === 'string' ? p.domain.trim() : '';
const country = typeof p.country === 'string' ? p.country.trim() : '';
if (!domain && !country) throw new Error('get_intel_timeline: pass domain or country');
if (country && !/^[A-Za-z]{2}$/.test(country)) throw new Error('country must be ISO 3166-1 alpha-2');
if (domain && !DOMAINS.has(domain)) throw new Error(`domain must be one of ${[...DOMAINS].join('|')}`); Type guard
function isMissingScopeError(e) {
return e instanceof Error && e.message.startsWith('get_intel_timeline requires at least one of domain');
} Try / catch
try {
return await client.callTool('get_intel_timeline', { domain, country, limit });
} catch (e) {
if (isMissingScopeError(e)) {
// Client bug: rebuild params ensuring domain/country survive trimming
return await client.callTool('get_intel_timeline', { ...p, domain: domain || 'conflict' });
}
throw e;
} Prevention
- Always set at least one of domain ('conflict'|'military'|'energy') or country (alpha-2) in the params template
- Trim and validate user-supplied scope strings before they reach tools/call
- Remember global/unscoped timelines are unsupported by design — the history store only indexes these two scopes
When it happens
Trigger: Calling tools/call get_intel_timeline with params that omit both domain and country, or pass whitespace-only strings (they trim to empty), or pass domain values outside the three indexed scopes... note: out-of-scope domain strings pass this check but will fail server-side with 400.
Common situations: Client built from a generic template that sends an empty params object. Whitespace-polluted values from user input. Assuming limit/from/to alone scope the query. Passing a region name ('middle-east') or full country name instead of the indexed domain enum or alpha-2 code.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Could not resolve to a country.
- country must be an ISO 3166-1 alpha-2 country code, e.g…
- get-intel-timeline: domain Required unless country is set…
- HTTP 400
- must be an ISO 3166-1 alpha-2 country code.
AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-08-21).
Data as JSON: /api/errors/68badc05d9cbad21.
Report an issue: GitHub.
Appendix: source
Thrown at api/mcp/registry/rpc-tools.ts:2300
// country_code returns `{error: "..."}` instead (result-level user-input error).
outputSchema: {
type: 'object',
properties: {
cached_at: { type: ['string', 'null'] },
stale: { type: 'boolean' },
country_code: { type: 'string' },
data: {
type: 'object',
properties: {
overview: { type: ['object', 'null'] },
categories: { type: ['object', 'array', 'null'] },
movers: { type: ['object', 'array', 'null'] },
retailerSpread: { type: ['object', 'array', 'null'] },
freshness: { type: ['object', 'null'] },
},
},
error: { type: 'string', description: 'Present only on user-input failure (missing/unknown country_code).' },
},
},
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
// Hybrid _execute (not a pure cache tool) because the cache keys are
// parameterised by country. Mirrors api/health.js::BOOTSTRAP_KEYS:55-59
// exactly so the U7 Tier-3 parity test treats every key as covered.
_coverageKeys: [
'consumer-prices:overview:ae',
'consumer-prices:categories:ae:30d',
'consumer-prices:movers:ae:30d',
'consumer-prices:retailer-spread:ae:essentials-ae',
'consumer-prices:freshness:ae',
],
_execute: async (params) => {
// Result-level errors (NOT throws) for user-input issues — the dispatcher
// maps thrown errors to JSON-RPC -32603 "Internal error", which is
// misleading for a clearly-user-side fault like a missing/unknown
// country_code. Returning {error: ...} surfaces a usable message via
// the normal tools/call result envelope.View on GitHub (pinned to e586b8b4b8)