ruvnet/ruflo · error

No nodes available for leader election

Error message

No nodes available for leader election

What it means

electLeader() requires at least one node in state.nodes; on an empty topology it throws before any election strategy runs. The fast paths (hierarchical queen lookup, centralized coordinator) also cannot produce a leader without nodes. This is a bootstrap-ordering guard, not a transient failure.

Source

Thrown at v3/@claude-flow/swarm/src/topology-manager.ts:196

    if (updates.status !== undefined) node.status = updates.status;
    if (updates.connections !== undefined) {
      node.connections = updates.connections;
      this.adjacencyList.set(agentId, new Set(updates.connections));
    }
    if (updates.metadata !== undefined) {
      node.metadata = { ...node.metadata, ...updates.metadata };
    }

    this.emit('node.updated', { agentId, updates });
  }

  getLeader(): string | undefined {
    return this.state.leader;
  }

  async electLeader(): Promise<string> {
    if (this.state.nodes.length === 0) {
      throw new Error('No nodes available for leader election');
    }

    // For hierarchical topology, the queen is the leader (O(1) lookup)
    if (this.config.type === 'hierarchical') {
      const queen = this.queenNode;
      if (queen) {
        this.state.leader = queen.agentId;
        return queen.agentId;
      }
    }

    // For centralized topology, the coordinator is the leader (O(1) lookup)
    if (this.config.type === 'centralized') {
      const coordinator = this.coordinatorNode;
      if (coordinator) {
        this.state.leader = coordinator.agentId;
        return coordinator.agentId;
      }

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. addNode() at least one node before electLeader()
  2. Guard the call: skip election while getState().nodes.length is 0
  3. Re-check swarm size after mass removals before triggering re-election

Example fix

// before
const topology = new TopologyManager(config);
await topology.electLeader(); // throws: no nodes

// after
await topology.addNode('queen-1', 'queen');
await topology.electLeader();
Defensive patterns

Strategy: validation

Validate before calling

function hasNodes(topology: TopologyManager): boolean {
  return topology.getState().nodes.length > 0;
}

if (hasNodes(topology)) {
  await topology.electLeader();
} else {
  // defer election until the first node joins
  topology.once('node.added', () => topology.electLeader());
}

Try / catch

try {
  await topology.electLeader();
} catch (err) {
  if (err instanceof Error && err.message === 'No nodes available for leader election') {
    await waitForFirstJoin(topology); // bootstrap race: retry once after a join
    return topology.electLeader();
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling electLeader() before any addNode(); calling it again after removeNode() drained the swarm; tests that tear down all nodes and then assert a new leader.

Common situations: Election triggered in startup hooks before agents join; shrinking a swarm to zero then re-electing; unit tests constructing a TopologyManager and immediately electing.

Related errors


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