{"record":{"id":"7dc5e0faea5dd6b5","repo":"koala73/worldmonitor","slug":"get-china-decision-signals-returned-an-invalid-can","errorCode":null,"errorMessage":"get-china-decision-signals returned an invalid canonical payload","messagePattern":"get-china-decision-signals returned an invalid canonical payload","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/mcp/registry/rpc-tools.ts","lineNumber":1130,"sourceCode":"      const url = `${base}/api/intelligence/v1/get-china-decision-signals`;\n      const auth = await buildAuthHeaders(context, 'GET', url, null);\n      const response = await fetchMcpDownstream(url, {\n        headers: { ...auth, 'User-Agent': 'worldmonitor-mcp-edge/1.0' },\n        signal: AbortSignal.timeout(12_000),\n      }, execution);\n      await assertMcpToolFetchOk(response, {\n        operation: 'get-china-decision-signals',\n        tool: 'get_china_decision_signals',\n        auth: context,\n        execution,\n      });\n      const wire = await response.json() as { payloadJson?: unknown };\n      if (typeof wire.payloadJson !== 'string') {\n        throw new Error('get-china-decision-signals returned no canonical payload');\n      }\n      const payload = JSON.parse(wire.payloadJson) as unknown;\n      if (!isChinaDecisionSignalSnapshot(payload)) {\n        throw new Error('get-china-decision-signals returned an invalid canonical payload');\n      }\n      return payload;\n    },\n    _coverageKeys: [\n      'china:policy-events:v1',\n      'military:cross-strait-activity:v1',\n      'military:cross-strait-activity-bootstrap:v1',\n      'market:china:corporate-disclosures:v1',\n      'intelligence:china-decision-signals:v1',\n    ],\n    _apiPaths: [\n      'GET /api/intelligence/v1/get-china-decision-signals',\n    ],\n  },\n  {\n    name: 'get_procurement_opportunities',\n    _outputBudgetBytes: 65536,\n    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.',","sourceCodeStart":1112,"sourceCodeEnd":1148,"githubUrl":"https://github.com/koala73/worldmonitor/blob/e586b8b4b80f595aa7ece295eec10d76f2921240/api/mcp/registry/rpc-tools.ts#L1112-L1148","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fetch the endpoint directly, extract payloadJson, JSON.parse it, and diff the top-level shape against isChinaDecisionSignalSnapshot's requirements","Update the guard or the producer so both agree on the snapshot schema, and ship them together","Check the writer of intelligence:china-decision-signals:v1 for partial/truncated writes if the shape looks right","Redeploy the stale side (registry vs intelligence handler) once aligned"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isInvalidCanonicalPayload(e) {\n  return e instanceof Error\n    && e.message === 'get-china-decision-signals returned an invalid canonical payload';\n}","tryCatchPattern":"try {\n  const snapshot = await fetchChinaDecisionSignals(context, execution);\n} catch (e) {\n  if (isInvalidCanonicalPayload(e)) {\n    // Schema drift between snapshot producer and MCP guard — inspect and realign, do not retry\n    await reportSchemaDrift('intelligence:china-decision-signals:v1');\n    throw e;\n  }\n  throw e;\n}","preventionTips":["Version the snapshot schema and gate deploys on the type guard passing in CI against a fixture","Update isChinaDecisionSignalSnapshot and the producer in the same change set","Log the parsed payload's top-level keys on guard failure to accelerate drift diagnosis"],"tags":["mcp","rpc","schema-drift","type-guard","china"],"backgroundTag":"schema-validation-failed","analyzedSha":"e586b8b4b80f595aa7ece295eec10d76f2921240","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}