ruvnet/ruflo · error · Error

String(parsed.error)

Error message

String(parsed.error)

What it means

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.

Solutions

  1. Read the coerced message to see what the gateway rejected (bad arguments, auth, rate limit) and correct the tool call.
  2. 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.
  3. Check gatewayUrl and arguments passed to gatewayRpc — a wrong gatewayUrl commonly produces error-shaped responses.
  4. Distinguish gateway-level errors from SDK isError failures; both funnel through throw new Error here.
Defensive patterns

Strategy: type-guard

When it happens

Trigger: Thrown at v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts:121 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of ruvnet/ruflo@9c61c86f06 (2026-09-22). Data as JSON: /api/errors/d260ad2d7a488d49. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts:121

  return (parsed.untrusted === true && parsed.data !== undefined
    ? (parsed.data as Record<string, unknown>)
    : parsed);
}

async function gatewayTool(name: string, args: Record<string, unknown>): Promise<unknown> {
  const { gatewayUrl, ...rest } = args;
  const r = (await gatewayRpc('tools/call', { name, arguments: rest }, gatewayUrl)) as { content?: Array<{ text?: string }>; isError?: boolean };
  const raw = r.content?.[0]?.text ?? '{}';
  // An isError result is the SDK's createToolError, whose message is raw text and
  // not JSON. Parsing first turns "private channels cannot be published…" into
  // "Unexpected token 'p'" — the same class of bug this parser exists to fix.
  if (r.isError) {
    let msg = raw;
    try { msg = String((parseGatewayText(raw) as { error?: unknown }).error ?? raw); } catch { /* raw text: use as-is */ }
    throw new Error(msg || 'gateway tool error');
  }
  const parsed = parseGatewayText(raw);
  if (parsed.error) throw new Error(String(parsed.error));
  return parsed;
}
async function gatewayResource(uri: string, gatewayUrl?: unknown): Promise<unknown> {
  const r = (await gatewayRpc('resources/read', { uri }, gatewayUrl)) as { contents?: Array<{ text?: string }> };
  return parseGatewayText(r.contents?.[0]?.text ?? '{}');
}
// Credential: intentionally env-only (a secret must never be a CLI flag — it would land in
// shell history / process lists). Registered in scripts/audit-env-var-precedence.mjs.
const adminToken = (): string | undefined => process.env.RUFLO_X_ADMIN_TOKEN;

export const xFederationTools: MCPTool[] = [
  {
    name: 'x_federation_sync',
    description:
      '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.',
    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.' } } },
    handler: async (input) => gatewayTool('federation_sync', input as Record<string, unknown>),
  },

View on GitHub (pinned to 9c61c86f06)