{"record":{"id":"d3211683f05d4967","repo":"ruvnet/ruflo","slug":"gateway-tool-error","errorCode":null,"errorMessage":"gateway tool error","messagePattern":"gateway tool error","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts","lineNumber":118,"sourceCode":" */\nexport function relayPayload(text: string): Record<string, unknown> {\n  const parsed = parseGatewayText(text);\n  return (parsed.untrusted === true && parsed.data !== undefined\n    ? (parsed.data as Record<string, unknown>)\n    : parsed);\n}\n\nasync function gatewayTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n  const { gatewayUrl, ...rest } = args;\n  const r = (await gatewayRpc('tools/call', { name, arguments: rest }, gatewayUrl)) as { content?: Array<{ text?: string }>; isError?: boolean };\n  const raw = r.content?.[0]?.text ?? '{}';\n  // An isError result is the SDK's createToolError, whose message is raw text and\n  // not JSON. Parsing first turns \"private channels cannot be published…\" into\n  // \"Unexpected token 'p'\" — the same class of bug this parser exists to fix.\n  if (r.isError) {\n    let msg = raw;\n    try { msg = String((parseGatewayText(raw) as { error?: unknown }).error ?? raw); } catch { /* raw text: use as-is */ }\n    throw new Error(msg || 'gateway tool error');\n  }\n  const parsed = parseGatewayText(raw);\n  if (parsed.error) throw new Error(String(parsed.error));\n  return parsed;\n}\nasync function gatewayResource(uri: string, gatewayUrl?: unknown): Promise<unknown> {\n  const r = (await gatewayRpc('resources/read', { uri }, gatewayUrl)) as { contents?: Array<{ text?: string }> };\n  return parseGatewayText(r.contents?.[0]?.text ?? '{}');\n}\n// Credential: intentionally env-only (a secret must never be a CLI flag — it would land in\n// shell history / process lists). Registered in scripts/audit-env-var-precedence.mjs.\nconst adminToken = (): string | undefined => process.env.RUFLO_X_ADMIN_TOKEN;\n\nexport const xFederationTools: MCPTool[] = [\n  {\n    name: 'x_federation_sync',\n    description:\n      'Fetch recent signature-verified coordination messages from the open x.ruv.io swarm federation (Nostr, #t=ruflo-swarm). Use when you need to see what other ruflo nodes across the internet have posted (PeerHello/Status/Task/Result/Claim*). Reading the relay directly is wrong because you would have to do NIP-42 auth yourself; the gateway does it and only returns events whose signatures verify.',","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/ruvnet/ruflo/blob/9c61c86f06b439af2a95085ae9bb0ca839662e41/v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts#L100-L136","documentation":"Thrown by `gatewayTool` after a successful `gatewayRpc` round-trip when the remote tool reported failure: either the MCP response has `isError: true` or the parsed content JSON contains an `error` field. Unlike error 26, the JSON-RPC layer succeeded — the tool executed but returned an error payload. Falls back to the literal 'gateway tool error' when the parsed body has no `error` message.","triggerScenarios":"Calling any `x_federation_*` gateway tool where the remote handler failed but responded via the MCP `isError` flag or an `{ error: ... }` content body: e.g. `federation_publish` rejecting a msgType/payload, `federation_invite_mint` failing server-side, or a tool returning an error result for invalid input while still returning HTTP 200 and a valid JSON-RPC result.","commonSituations":"Publishing a message with a payload the gateway's schema rejects; minting invites when the relay-side write fails; a gateway version mismatch where the remote tool's response shape changed (isError set but no parseable message); permissions/auth failures inside the tool that surface as tool-level errors rather than RPC errors.","solutions":["Inspect the thrown message — if it is the generic fallback 'gateway tool error', enable verbose logging or call the gateway directly to see the tool's full content payload.","Validate the tool's input against its declared inputSchema before invoking (e.g. required `msgType`/`payload` for federation_publish).","For admin-gated tools, ensure RUFLO_X_ADMIN_TOKEN is set and valid (error 28/29 catch the missing-token case earlier; an invalid token may surface here).","Retry on transient gateway-side failures; check gateway status/logs if errors persist.","Confirm gateway/tool version compatibility if the response shape changed after a gateway upgrade."],"exampleFix":"// before\nconst out = await gatewayTool('federation_publish', { msgType: 'Status' });\n// after: guard required fields first\nif (!msgType || !payload) throw new Error('federation_publish requires msgType and payload');\nconst out = await gatewayTool('federation_publish', { msgType, payload });","handlingStrategy":"type-guard","validationCode":"const required = ['msgType', 'payload']; // per tool inputSchema\nfor (const k of required) {\n  if (!(k in args)) throw new Error(`x_federation tool missing required field: ${k}`);\n}","typeGuard":"type ToolResponse = { content?: Array<{ text?: string }>; isError?: boolean };\nfunction toolReportedError(r: ToolResponse, parsed: Record<string, unknown>): boolean {\n  return r.isError === true || 'error' in parsed;\n}","tryCatchPattern":"try {\n  const parsed = await gatewayTool(name, args);\n} catch (e) {\n  if (e instanceof Error && e.message === 'gateway tool error') {\n    console.error('tool failed without a message — inspect full gateway content payload / gateway logs');\n  } else if (e instanceof Error) {\n    console.error('tool error:', e.message);\n  } else throw e;\n}","preventionTips":["Validate inputs against each x_federation tool's declared inputSchema before invoking.","Handle both error channels: the JSON-RPC error (26) and the MCP isError/content error (this one).","Capture the full tool content on failure for diagnostics instead of only the thrown message.","Keep gateway and CLI versions aligned so response shapes stay stable.","Retry transient tool failures with backoff; escalate persistent ones to gateway logs."],"tags":["mcp","gateway","tool-error","federation"],"backgroundTag":"unexpected-response-shape","analyzedSha":"9c61c86f06b439af2a95085ae9bb0ca839662e41","analyzedAt":"2026-09-22T05:44:27.648Z","contentChangedAt":"2026-09-22T05:44:27.648Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}