koala73/worldmonitor · error
get-china-decision-signals returned no canonical payload
Error message
get-china-decision-signals returned no canonical payload
What it means
Plain Error thrown in the get-china-decision-signals tool (api/mcp/registry/rpc-tools.ts:703): the downstream fetch passed auth and returned 200 (assertMcpToolFetchOk did not throw), but response.json() lacks a string payloadJson field. This is a contract violation between the MCP tool and the GET /api/intelligence/v1/get-china-decision-signals handler — the canonical wire shape is { payloadJson: string } and anything else (error envelope with 200, unexpected JSON, missing field) fails fast here rather than corrupting downstream snapshot logic.
Solutions
- curl GET /api/intelligence/v1/get-china-decision-signals directly with valid auth and inspect the JSON — confirm payloadJson exists and is a string
- If the handler's response shape changed, update the wire expectation and the isChinaDecisionSignalSnapshot guard together, then redeploy both surfaces
- Check for intermediaries returning 200-wrapped error bodies (gateway/proxy config)
- Redeploy the lagging surface so registry and handler are version-aligned
Defensive patterns
Strategy: try-catch
Type guard
function isMissingCanonicalPayload(e) {
return e instanceof Error
&& e.message === 'get-china-decision-signals returned no canonical payload';
} Try / catch
try {
const snapshot = await fetchChinaDecisionSignals(context, execution);
} catch (e) {
if (isMissingCanonicalPayload(e)) {
// Contract drift between registry and handler — not transient. Probe once to confirm:
const probe = await fetch(url, { headers: auth }).then(r => r.json());
logContractMismatch('get-china-decision-signals', Object.keys(probe));
throw new Error('Downstream contract drift; payloadJson missing', { cause: e });
}
throw e;
} Prevention
- Add a contract test asserting the endpoint returns { payloadJson: string } on 200
- Deploy the MCP registry and intelligence handler as one atomic unit
- Treat this error as non-retryable — retrying only re-fetches the same wrong shape
When it happens
Trigger: Deploying the MCP registry and the intelligence API handler out of sync: handler version that does not yet emit payloadJson, an intermediary (proxy/WAF) returning a 200 JSON body of its own, or a handler bug returning an error envelope without the right status code.
Common situations: Partial deploy where api/ routes lag the MCP registry (or vice versa). Staged rollout hitting mixed versions. A gateway rewrite that swallows the handler's error status and returns 200 with an error body. Local dev proxying to a stale backend.
Related errors
- get-china-decision-signals returned an invalid canonical…
- Could not resolve to a country.
- country must be an ISO 3166-1 alpha-2 country code, e.g…
- get-country-intel-brief HTTP
- get-intel-timeline: domain Required unless country is set…
AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-08-21).
Data as JSON: /api/errors/63a506120cde3161.
Report an issue: GitHub.
Appendix: source
Thrown at api/mcp/registry/rpc-tools.ts:1126
},
},
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
_execute: async (_params, base, context, execution) => {
const url = `${base}/api/intelligence/v1/get-china-decision-signals`;
const auth = await buildAuthHeaders(context, 'GET', url, null);
const response = await fetchMcpDownstream(url, {
headers: { ...auth, 'User-Agent': 'worldmonitor-mcp-edge/1.0' },
signal: AbortSignal.timeout(12_000),
}, execution);
await assertMcpToolFetchOk(response, {
operation: 'get-china-decision-signals',
tool: 'get_china_decision_signals',
auth: context,
execution,
});
const wire = await response.json() as { payloadJson?: unknown };
if (typeof wire.payloadJson !== 'string') {
throw new Error('get-china-decision-signals returned no canonical payload');
}
const payload = JSON.parse(wire.payloadJson) as unknown;
if (!isChinaDecisionSignalSnapshot(payload)) {
throw new Error('get-china-decision-signals returned an invalid canonical payload');
}
return payload;
},
_coverageKeys: [
'china:policy-events:v1',
'military:cross-strait-activity:v1',
'military:cross-strait-activity-bootstrap:v1',
'market:china:corporate-disclosures:v1',
'intelligence:china-decision-signals:v1',
],
_apiPaths: [
'GET /api/intelligence/v1/get-china-decision-signals',
],
},View on GitHub (pinned to e586b8b4b8)