{"record":{"id":"f057dad85d1d98d7","repo":"ruvnet/ruflo","slug":"only-leader-can-propose-values","errorCode":null,"errorMessage":"Only leader can propose values","messagePattern":"Only leader can propose values","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/swarm/src/consensus/raft.ts","lineNumber":181,"sourceCode":"\n  addPeer(peerId: string): void {\n    this.peers.set(peerId, {\n      id: peerId,\n      state: 'follower',\n      currentTerm: 0,\n      log: [],\n      commitIndex: 0,\n      lastApplied: 0,\n    });\n  }\n\n  removePeer(peerId: string): void {\n    this.peers.delete(peerId);\n  }\n\n  async propose(value: unknown): Promise<ConsensusProposal> {\n    if (this.node.state !== 'leader') {\n      throw new Error('Only leader can propose values');\n    }\n\n    this.proposalCounter++;\n    const proposalId = `raft_${this.node.id}_${this.proposalCounter}`;\n\n    const proposal: ConsensusProposal = {\n      id: proposalId,\n      proposerId: this.node.id,\n      value,\n      term: this.node.currentTerm,\n      timestamp: new Date(),\n      votes: new Map(),\n      status: 'pending',\n    };\n\n    // Add to local log\n    const logEntry: RaftLogEntry = {\n      term: this.node.currentTerm,","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/swarm/src/consensus/raft.ts#L163-L199","documentation":"RaftConsensus.propose() requires this.node.state === 'leader'; followers and candidates throw. Leadership comes only from Raft election, and the ConsensusEngine facade forwards the 'leader.elected' event and exposes isLeader()/getLeaderId() so callers can check before proposing.","triggerScenarios":"Proposing before the election has finished (node is 'follower' or 'candidate'); proposing after this node lost leadership through a term change or partition; every node in a test cluster proposing simultaneously instead of only the elected leader.","commonSituations":"Symmetric deployments where each replica proposes on a timer; leadership churn under network partitions; single-node tests where election was never triggered; proposals routed to a stale leader id.","solutions":["Wait for the 'leader.elected' event, then propose only from the leader (check engine.isLeader()).","Forward values to the current leader (engine.getLeaderId()) when this node is a follower.","Retry the proposal after an election timeout — leadership may have moved to this node.","In tests, drive the election explicitly before asserting propose() succeeds."],"exampleFix":"// before — propose from any node\nawait raft.propose(value); // throws unless this node is leader\n\n// after — gate on leadership\nlet leaderId: string | undefined;\nengine.on('leader.elected', ({ leaderId: id }) => { leaderId = id; });\nif (engine.isLeader()) {\n  await raft.propose(value);\n} else {\n  await forwardToLeader(leaderId, value);\n}","handlingStrategy":"validation","validationCode":"// the facade exposes leadership explicitly\nif (!engine.isLeader()) {\n  const leaderId = engine.getLeaderId();\n  if (!leaderId) throw new Error('no leader elected yet');\n  await forwardToLeader(leaderId, value);\n} else {\n  await engine.propose(value);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await raft.propose(value);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Only leader can propose values') {\n    // wait for the next 'leader.elected' event and retry, or forward to the leader\n  } else {\n    throw e;\n  }\n}","preventionTips":["Check engine.isLeader() (or cache leaderId from 'leader.elected') before every propose.","Centralize proposals in a leader-aware router instead of proposing from all replicas.","Expect leadership churn: retry proposals after elections instead of treating them as fatal."],"tags":["consensus","raft","leader-election","proposal"],"backgroundTag":"not-cluster-leader","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}