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

  1. Call GET /api/intelligence/v1/get-china-decision-signals directly and inspect the response body — confirm payloadJson is present and string-typed.
  2. 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.
  3. Verify no middleware/gateway is rewriting the response body between handler and MCP edge function.
  4. 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

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


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/63a506120cde3161. Report an issue: GitHub.