koala73/worldmonitor · error · Error
get-china-decision-signals returned no canonical payload
Error message
get-china-decision-signals returned no canonical payload
What it means
Thrown by the get_china_decision_signals MCP tool when the downstream handler returned HTTP 200 (it passed assertMcpToolFetchOk) but the response body lacks a string-typed payloadJson field. The canonical contract is that the handler wraps the full snapshot in { payloadJson: '<JSON string>' }; a missing or non-string payloadJson means the handler has a contract regression or returned an unexpected envelope shape.
Source
Thrown at api/mcp/registry/rpc-tools.ts:615
},
},
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 fetch(url, {
headers: { ...auth, 'User-Agent': 'worldmonitor-mcp-edge/1.0' },
signal: AbortSignal.timeout(12_000),
});
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 ffec79ac33)
Solutions
- Call GET /api/intelligence/v1/get-china-decision-signals directly and inspect the response body — confirm payloadJson is present and string-typed.
- Check for a version mismatch between the handler (server/worldmonitor/) and the MCP tool (api/mcp/registry/rpc-tools.ts) if either was recently changed.
- Verify no middleware/gateway is rewriting the response body between handler and MCP edge function.
- If the handler itself is returning an unexpected shape, fix it to always emit { payloadJson: JSON.stringify(snapshot) } on success.
Defensive patterns
Strategy: try-catch
Validate before calling
// Before relying on the result, probe the handler directly
const probe = await fetch('/api/intelligence/v1/get-china-decision-signals', { headers: authHeaders });
const body = await probe.json();
if (typeof body.payloadJson !== 'string') {
throw new Error('Handler contract regression: payloadJson missing or non-string');
} Type guard
function isNoCanonicalPayloadError(e: unknown): boolean {
return e instanceof Error && e.message.includes('returned no canonical payload');
} Try / catch
try {
const snapshot = await callMcpTool('get_china_decision_signals', {});
} catch (e) {
if (e instanceof Error && e.message.includes('returned no canonical payload')) {
// Handler returned unexpected shape — report contract regression, do not retry blindly
logContractRegression('get-china-decision-signals', e);
} else throw e;
} Prevention
- Run the china-decision-signals parity audit (scripts/audit-china-decision-parity.mjs) after any handler or seeder change.
- Monitor for this error in Sentry — it indicates a deploy version mismatch, not a transient issue.
- Do not retry automatically — a contract regression will fail identically until the handler is fixed.
When it happens
Trigger: The /api/intelligence/v1/get-china-decision-signals handler returned 200 but with a body where payloadJson is undefined, null, a number, or an object — not a JSON-encoded string. This happens on a handler version mismatch, a partial deploy where the handler changed its response shape, or a gateway/proxy that stripped or rewrote the body.
Common situations: Deploying a new handler version that changed the envelope shape before the MCP tool was updated (or vice versa); a gateway or middleware that re-serialized the response and dropped payloadJson; the handler returning an empty 200 on an internal edge case instead of a proper error status.
Related errors
- get-china-decision-signals returned an invalid canonical pay
- get-country-intel-brief HTTP ${res.status}${code ? `: ${code
- get-vessel-snapshot HTTP ${res.status}${detail ? ` — ${detai
- HTTP %d: %s
- serverUrl host is not allowed
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/63a506120cde3161.
Report an issue: GitHub.