ruvnet/ruflo · error · Error

PEER_UNKNOWN

PEER_UNKNOWN

Error message

PEER_UNKNOWN: ${targetNodeId}

What it means

Raised when the federation layer attempts delivery of an envelope over a real transport but the target cannot be resolved: resolveAddress(targetNodeId) returns no address or discovery.getPeer() has no registry entry for the node. Note the contrast: with no transport configured the send is silently dropped as an in-process noop; PEER_UNKNOWN means routing was attempted and discovery does not know the peer.

Source

Thrown at v3/@claude-flow/plugin-agent-federation/src/plugin.ts:373

      sendToNode: async (targetNodeId, envelope) => {
        // ADR-104: real wire send via the loaded transport. If the
        // transport failed to load OR the peer's address can't be
        // resolved, log + return (the upstream RoutingService.send
        // already wraps this in try/catch and returns a RoutingResult
        // with the error to the caller).
        if (!transport) {
          context.logger.debug(
            `Federation send (in-process noop): ${envelope.envelopeId} → ${targetNodeId}`,
          );
          return;
        }
        const address = resolveAddress(targetNodeId);
        const peer = discovery.getPeer(targetNodeId);
        if (!address || !peer) {
          context.logger.warn(
            `Federation send aborted: peer ${targetNodeId} not in discovery registry`,
          );
          throw new Error(`PEER_UNKNOWN: ${targetNodeId}`);
        }
        const signatureVersion = selectEnvelopeSignatureVersion(
          signatureMode,
          peer.capabilities.supportedProtocols,
          envelope.messageType,
        );
        // Build the AgentMessage WITHOUT signature first, canonicalize
        // the bytes, sign them, then attach the signature to metadata.
        // The receiver runs the same canonicalization and verifies
        // against this node's published public key.
        const baseMessage: AgentMessage = {
          id: envelope.envelopeId,
          type: envelope.messageType,
          payload: envelope as unknown,
          metadata: {
            sourceNodeId: envelope.sourceNodeId,
            targetNodeId: envelope.targetNodeId,
            sessionId: envelope.sessionId,

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Verify the exact targetNodeId against the discovery registry (peer list/status tooling)
  2. Ensure the peer node is online and re-advertised itself (federation_init plus discovery), or add it to staticPeers
  3. If the peer just joined, retry after a short backoff to allow discovery propagation

Example fix

// before
await client.callTool('federation_send', { targetNodeId: 'node-b', envelope }); // PEER_UNKNOWN: node-b

// after
const peers = await client.callTool('federation_peers', {});
if (!peerIds(peers).includes('node-b')) {
  await waitForDiscovery('node-b'); // or add node-b to staticPeers
}
await client.callTool('federation_send', { targetNodeId: 'node-b', envelope });
Defensive patterns

Strategy: try-catch

Validate before calling

const known = new Set<string>();
subscribeToDiscoveryUpdates(
  (peer) => known.add(peer.nodeId),
  (peer) => known.delete(peer.nodeId),
);
if (!known.has(targetNodeId)) {
  throw new Error(`refusing send: ${targetNodeId} not in known peers; refresh discovery first`);
}

Try / catch

try {
  await federationSend(envelope, targetNodeId);
} catch (e) {
  if (e instanceof Error && /^PEER_UNKNOWN: /.test(e.message)) {
    const nodeId = e.message.slice('PEER_UNKNOWN: '.length);
    await refreshDiscovery();
    if (!isPeerKnown(nodeId)) deadLetter(envelope, nodeId); // do not blindly retry
  } else throw e;
}

Prevention

When it happens

Trigger: Sending to a nodeId that never appeared via discovery, a peer that disconnected and was evicted from the registry, a node missing from staticPeers, or a mistyped target ID — while a transport is configured.

Common situations: Race where the remote node just joined and discovery has not propagated yet; the peer process crashed and its entry expired; clusters split across environments or mismatched staticPeers lists.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/ef19287e4d241d45. Report an issue: GitHub.