{"record":{"id":"d260ad2d7a488d49","repo":"ruvnet/ruflo","slug":"string-parsed-error","errorCode":null,"errorMessage":"String(parsed.error)","messagePattern":"String\\(parsed\\.error\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts","lineNumber":121,"sourceCode":"  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.',\n    inputSchema: { type: 'object', properties: { ...gatewayArg, sinceSeconds: { type: 'number', description: 'Look-back window (default 3600).' }, limit: { type: 'number', description: 'Max messages (default 100).' }, type: { type: 'string', description: 'Optional message type filter, e.g. Task.' } } },\n    handler: async (input) => gatewayTool('federation_sync', input as Record<string, unknown>),\n  },","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/ruvnet/ruflo/blob/9c61c86f06b439af2a95085ae9bb0ca839662e41/v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts#L103-L139","documentation":"gatewayTool throws this after a successful (non-isError) gateway tools/call: it parses the first content text via parseGatewayText and, if the parsed JSON has a truthy `error` property, converts it with String() into the thrown Error message. This is a generic guard, not a specific bug: the faulting input is any gateway response shaped `{error: <unknown>}` — the x.ruv.io gateway itself reporting a per-request failure (bad arguments, auth, rate limit) rather than the SDK's isError path. String() coerces non-string error values so callers always get a readable message; it can yield '[object Object]' if the gateway sends a non-string error object.","triggerScenarios":"Thrown at v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts:121 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Read the coerced message to see what the gateway rejected (bad arguments, auth, rate limit) and correct the tool call.","If the message is '[object Object]', inspect the gateway's raw response to recover the structured error and consider JSON.stringify-ing parsed.error at the throw site.","Check gatewayUrl and arguments passed to gatewayRpc — a wrong gatewayUrl commonly produces error-shaped responses.","Distinguish gateway-level errors from SDK isError failures; both funnel through throw new Error here."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}