koala73/worldmonitor · error · Error
get_intel_timeline requires at least one of domain ("conflic
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
Thrown by the get_intel_timeline MCP tool as a client-side pre-flight validation before the server-side handler is even called. The tool requires at least one indexed scope — either a domain ('conflict', 'military', or 'energy') or a country (ISO 3166-1 alpha-2) — because those are the only two indexes on the history store. Without one, the handler would return a 400; this check turns that opaque failure into an actionable message and saves the round-trip. The handler remains the enforcing authority.
Source
Thrown at api/mcp/registry/rpc-tools.ts:1861
},
outputSchema: {
type: 'object',
required: ['records', 'partial', 'upstreamUnavailable'],
properties: {
records: { type: 'array', description: 'Scoped history, newest first.', items: INTEL_HISTORY_RECORD_SCHEMA },
partial: { type: 'boolean', description: 'True when the bounded post-filter window may omit older matching events.' },
upstreamUnavailable: { type: 'boolean', description: 'True when the history store could not be reached. `records` is then empty because the read failed — never read that as "nothing happened in this window".' },
},
},
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
_execute: async (params, base, context, execution) => {
// Scope is mandatory server-side (a 400 from the handler). Checking it
// here turns an opaque downstream failure into an actionable message and
// saves the round-trip; the handler stays the enforcing authority.
const domain = typeof params.domain === 'string' ? params.domain.trim() : '';
const country = typeof params.country === 'string' ? params.country.trim() : '';
if (!domain && !country) {
throw new Error('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.');
}
const query = new URLSearchParams();
addStringParam(query, 'domain', domain);
addStringParam(query, 'country', country);
addIntelHistoryNumber(query, 'from', params.from);
addIntelHistoryNumber(query, 'to', params.to);
addIntelHistoryNumber(query, 'limit', Math.min(Number(params.limit ?? MCP_HISTORY_TIMELINE_MAX_LIMIT), MCP_HISTORY_TIMELINE_MAX_LIMIT));
const url = `${base}/api/intelligence/v1/get-intel-timeline?${query}`;
const auth = await buildAuthHeaders(context, 'GET', url, null);
// No embedding on this path — one store read, so the tighter budget.
const response = await fetch(url, {
headers: { ...auth, 'User-Agent': 'worldmonitor-mcp-edge/1.0' },
signal: AbortSignal.timeout(8_000),
});
await assertMcpToolFetchOk(response, {
operation: 'get-intel-timeline',View on GitHub (pinned to ffec79ac33)
Solutions
- Provide at least one of domain or country: domain must be one of 'conflict', 'military', 'energy'; country must be an ISO 3166-1 alpha-2 code.
- If you want all domains for a country, pass only country (e.g. country='US') — the handler applies the country index across all domains.
- If you want a cross-country domain view, pass only domain (e.g. domain='military').
- You can pass both domain AND country to narrow to one domain within one country.
Example fix
// before — no scope, throws immediately
tool: 'get_intel_timeline', params: { limit: 50 }
// after — scoped to military domain
tool: 'get_intel_timeline', params: { domain: 'military', limit: 50 } Defensive patterns
Strategy: validation
Validate before calling
// Validate scope params BEFORE calling get_intel_timeline
const VALID_DOMAINS = ['conflict', 'military', 'energy'];
const domain = typeof params.domain === 'string' ? params.domain.trim() : '';
const country = typeof params.country === 'string' ? params.country.trim() : '';
if (!domain && !country) {
throw new Error('Provide at least one of domain or country');
}
if (country && !/^[A-Z]{2}$/.test(country)) {
throw new Error('country must be ISO 3166-1 alpha-2');
}
if (domain && !VALID_DOMAINS.includes(domain)) {
throw new Error(`domain must be one of: ${VALID_DOMAINS.join(', ')}`);
} Prevention
- Always pass at least one of domain or country — both are optional in the schema but mandatory in practice.
- Domain must be exactly 'conflict', 'military', or 'energy' (validated server-side too).
- Country must be a 2-letter uppercase ISO code.
- This is a client-side check that saves a round-trip — the server enforces the same rule with a 400.
When it happens
Trigger: Calling get_intel_timeline with neither domain nor country provided (both omitted, empty strings, or whitespace-only). For example: params: {} or params: { limit: 100 } or params: { domain: ' ', country: '' }. The validation trims the values first, so whitespace-only strings are treated as absent.
Common situations: An agent or caller that calls get_intel_timeline with only optional params (from, to, limit) forgetting the required scope; passing a domain value outside the allowed set (e.g. domain='geopolitics') — note this does NOT trigger THIS error (the string is non-empty) but would fail server-side; copy-paste from a template that omitted the scope fields.
Related errors
- serverUrl DNS resolution returned no addresses
- Seeded world brief unavailable (${result.reason})
- INVALID_NAME
- INVALID_PREFIX
- INVALID_HASH
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/68badc05d9cbad21.
Report an issue: GitHub.