{"record":{"id":"b6bd2fb474603661","repo":"ruvnet/ruflo","slug":"federationtransport-no-address-for-peer-to","errorCode":null,"errorMessage":"FederationTransport: no address for peer ${to}","messagePattern":"FederationTransport: no address for peer (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/swarm/src/consensus/federation-transport.ts","lineNumber":156,"sourceCode":"  }\n\n  peers(): readonly string[] {\n    return this.peerIdsFn().filter(id => id !== this.nodeId);\n  }\n\n  private stamp(msg: Omit<ConsensusMessage, 'from'>): ConsensusMessage {\n    const base: Omit<ConsensusMessage, 'signature'> = {\n      ...msg,\n      from: this.nodeId,\n      seq: this.keyPair ? ++this.seqCounter : msg.seq,\n    };\n    return this.keyPair ? { ...base, signature: signMessage(base, this.keyPair.privateKeyPem) } : base;\n  }\n\n  async send(to: string, msg: Omit<ConsensusMessage, 'from'>, timeoutMs?: number): Promise<ConsensusReply> {\n    if (this.closed) throw new Error('FederationTransport: closed');\n    const addr = this.addressOf(to);\n    if (!addr) throw new Error(`FederationTransport: no address for peer ${to}`);\n    const corr = randomBytes(8).toString('hex');\n    const stamped = this.stamp({ ...msg, to });\n    const t = timeoutMs ?? this.defaultTimeoutMs;\n    return new Promise<ConsensusReply>((resolve, reject) => {\n      const timer = setTimeout(() => { this.pending.delete(corr); reject(new Error(`FederationTransport: send to ${to} timed out (${t}ms)`)); }, t);\n      this.pending.set(corr, { resolve, reject, timer });\n      this.wire.send(addr, { type: 'consensus', payload: { corr, msg: stamped } as WireEnvelope, streamId: this.streamId })\n        .catch((e) => { clearTimeout(timer); this.pending.delete(corr); reject(e instanceof Error ? e : new Error(String(e))); });\n    });\n  }\n\n  async broadcast(msg: Omit<ConsensusMessage, 'from'>): Promise<void> {\n    if (this.closed) throw new Error('FederationTransport: closed');\n    const stamped = this.stamp(msg);\n    await Promise.allSettled(this.peers().map(async (to) => {\n      const addr = this.addressOf(to);\n      if (!addr) return;\n      await this.wire.send(addr, { type: 'consensus', payload: { msg: stamped } as WireEnvelope, streamId: this.streamId }).catch(() => {});","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/swarm/src/consensus/federation-transport.ts#L138-L174","documentation":"FederationTransport.send(to, ...) resolves the recipient through the caller-supplied addressOf callback passed in FederationTransportOptions. When addressOf(to) returns undefined the send fails before anything touches the wire — the transport refuses to send to peers it has no address mapping for. The mapping and the peerIds list are both injected, so this error always points at the host application's membership/addressing table, not at remote state.","triggerScenarios":"Sending to a node id with no entry in the addressOf mapping; a typo or case mismatch in the peer id; the peer was removed from the address table during membership changes; federation membership updated on the consensus side but not in the address mapping (or vice versa).","commonSituations":"Cluster membership and transport addresses drifting apart; environment-specific node names (node-7 in staging, n7 in prod); copy-pasted ids from another cluster; a peer joined but its address was never published to this node's config.","solutions":["Add the missing entry so addressOf(nodeId) returns the peer's WS/QUIC address.","Pre-validate the target with the same addressOf function used to build the options before calling send.","Re-sync the membership/address table (single source of truth for peerIds + addresses) and retry.","Check for id normalization issues: exact string match, casing, whitespace."],"exampleFix":"// before — the injected addressOf has no entry for 'node-7'\nconst transport = new FederationTransport(wire, {\n  nodeId: 'node-1',\n  addressOf: (id) => addresses[id], // addresses['node-7'] === undefined\n  peerIds: () => Object.keys(addresses),\n});\nawait transport.send('node-7', msg); // throws\n\n// after — keep the address table in sync with membership\naddresses['node-7'] = 'wss://node-7.example/agentic-flow';\nawait transport.send('node-7', msg);","handlingStrategy":"validation","validationCode":"// you supply addressOf — pre-check it with the same function before send\nconst address = addressOf(nodeId);\nif (!address) {\n  throw new Error(`no address for ${nodeId}; refresh the membership mapping first`);\n}\nawait transport.send(nodeId, msg);","typeGuard":null,"tryCatchPattern":"try {\n  await transport.send(nodeId, msg);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('FederationTransport: no address for peer')) {\n    // refresh membership/address table, then retry once; otherwise drop the peer\n  } else {\n    throw e;\n  }\n}","preventionTips":["Drive both addressOf and peerIds from a single membership source so they cannot diverge.","Validate peer ids at the boundary where membership changes (join/leave) rather than at send time.","Log unknown-peer failures — broadcast() silently skips addressless peers, which can mask partial membership."],"tags":["transport","peer-routing","federation","membership"],"backgroundTag":"unknown-peer","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}