koala73/worldmonitor · error

get-china-decision-signals returned an invalid canonical…

Error message

get-china-decision-signals returned an invalid canonical payload

What it means

Plain Error thrown in the get-china-decision-signals tool (api/mcp/registry/rpc-tools.ts:707): the wire payloadJson string was present and JSON.parse succeeded, but the parsed value failed the isChinaDecisionSignalSnapshot type guard. The MCP side validates the canonical snapshot shape before returning it; a parse-OK-but-invalid payload means the producer wrote a snapshot that no longer matches the guard — schema drift or a malformed write.

Solutions

  1. Fetch the endpoint directly, extract payloadJson, JSON.parse it, and diff the top-level shape against isChinaDecisionSignalSnapshot's requirements
  2. Update the guard or the producer so both agree on the snapshot schema, and ship them together
  3. Check the writer of intelligence:china-decision-signals:v1 for partial/truncated writes if the shape looks right
  4. Redeploy the stale side (registry vs intelligence handler) once aligned
Defensive patterns

Strategy: try-catch

Type guard

function isInvalidCanonicalPayload(e) {
  return e instanceof Error
    && e.message === 'get-china-decision-signals returned an invalid canonical payload';
}

Try / catch

try {
  const snapshot = await fetchChinaDecisionSignals(context, execution);
} catch (e) {
  if (isInvalidCanonicalPayload(e)) {
    // Schema drift between snapshot producer and MCP guard — inspect and realign, do not retry
    await reportSchemaDrift('intelligence:china-decision-signals:v1');
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: The producer that writes intelligence:china-decision-signals:v1 changed its snapshot schema (added/renamed/retyped required fields) while the MCP guard still expects the old shape, or a bug wrote a partial/malformed snapshot that serializes fine but fails structure validation.

Common situations: Producer deployed ahead of MCP registry without updating the guard (or the reverse). New optional-to-required field promotion in the snapshot. A truncated write under producer timeout. Version skew in worktrees/preview deployments.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-08-21). Data as JSON: /api/errors/7dc5e0faea5dd6b5. Report an issue: GitHub.

Appendix: source

Thrown at api/mcp/registry/rpc-tools.ts:1130

      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',
    ],
  },
  {
    name: 'get_procurement_opportunities',
    _outputBudgetBytes: 65536,
    description: 'Search open global public-procurement opportunities through the canonical Pro route. Default output is 10 compact records (maximum 25), without descriptions or submission/eligibility payloads. automationFit is keyword relevance evidence only, never bidding eligibility; participationMode "unknown" remains unknown.',

View on GitHub (pinned to e586b8b4b8)