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
- 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.
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)