koala73/worldmonitor · error · Error

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

Error message

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

What it means

Thrown by the get_china_decision_signals MCP tool when the response carried a string payloadJson but after JSON.parse the result failed the isChinaDecisionSignalSnapshot type guard (defined in shared/china-decision-signals.ts). This means the producer wrote a payload that does not conform to the canonical snapshot schema — a field is missing, mistyped, or structurally changed.

Source

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

      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',
    ],
  },
  {
    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 ffec79ac33)

Solutions

  1. Inspect the actual snapshot in Redis (the intelligence:china-decision-signals:v1 key) and diff its fields against isChinaDecisionSignalSnapshot in shared/china-decision-signals.ts.
  2. Run scripts/audit-china-decision-parity.mjs which validates the parity between the snapshot shape and the type guard.
  3. If the seeder changed the shape, update both the seeder output and the type guard together so they agree.
  4. Re-seed the snapshot after fixing the producer; the stale/malformed key will be overwritten.
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the snapshot shape client-side using the shared guard (if available)
import { isChinaDecisionSignalSnapshot } from '../shared/china-decision-signals';
const probe = await fetch('/api/intelligence/v1/get-china-decision-signals', { headers: authHeaders });
const body = await probe.json();
const payload = JSON.parse(body.payloadJson);
if (!isChinaDecisionSignalSnapshot(payload)) {
  throw new Error('Snapshot fails schema guard — seeder/handler version mismatch');
}

Type guard

function isInvalidCanonicalPayloadError(e: unknown): boolean {
  return e instanceof Error && e.message.includes('returned an invalid canonical payload');
}

Try / catch

try {
  const snapshot = await callMcpTool('get_china_decision_signals', {});
} catch (e) {
  if (e instanceof Error && e.message.includes('returned an invalid canonical payload')) {
    // Schema regression in the seeder output — do not retry, report it
    logSchemaRegression('china-decision-signals', e);
  } else throw e;
}

Prevention

When it happens

Trigger: The china-decision-signals seeder wrote a snapshot to Redis that violates the isChinaDecisionSignalSnapshot schema — e.g. a required field (like a nested array or timestamp) is absent, a field type changed (string vs number), or the snapshot was partially written. The handler faithfully wrapped it in payloadJson, but the MCP layer rejects it because the shape does not match the canonical contract.

Common situations: A seeder version change that altered the snapshot shape without updating the type guard (or vice versa); a partial Redis write where the seeder crashed mid-serialization; a upstream source (cross-strait activity, policy events) schema change that propagated into the snapshot undetected.

Related errors


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