ruvnet/ruflo · error · Error
Proposal ${proposalId} not found
Error message
Proposal ${proposalId} not found What it means
RaftConsensus.vote(proposalId, vote) looks the proposal up in the local proposals map; an id this node never created or received throws 'Proposal ... not found'. Note the adjacent behavior: if the proposal exists but is no longer 'pending', vote() returns silently — so this error specifically means the proposal id is unknown locally, not merely late.
Source
Thrown at v3/@claude-flow/swarm/src/consensus/raft.ts:225
// Leader votes for itself
proposal.votes.set(this.node.id, {
voterId: this.node.id,
approve: true,
confidence: 1.0,
timestamp: new Date(),
});
// Replicate to followers
await this.replicateToFollowers(logEntry);
return proposal;
}
async vote(proposalId: string, vote: ConsensusVote): Promise<void> {
const proposal = this.proposals.get(proposalId);
if (!proposal) {
throw new Error(`Proposal ${proposalId} not found`);
}
if (proposal.status !== 'pending') {
return;
}
proposal.votes.set(vote.voterId, vote);
// Check if we have consensus
await this.checkConsensus(proposalId);
}
async awaitConsensus(proposalId: string): Promise<ConsensusResult> {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const checkInterval = setInterval(() => {
const proposal = this.proposals.get(proposalId);View on GitHub (pinned to fa13ee4ad6)
Solutions
- Use the exact proposal object/id returned by propose() when voting.
- Guard with a lookup first (e.g. engine.getProposal(proposalId)) and drop or defer votes for unknown ids.
- Ensure replication delivers a proposal to a node before it is expected to vote on it.
- After a restart, expect unknown-proposal errors for pre-restart proposals — rebuild state or ignore stale votes.
Example fix
// before
await raft.vote(proposalId, vote); // proposalId unknown on this node
// after — guard with a local lookup
if (!engine.getProposal(proposalId)) {
throw new Error(`proposal ${proposalId} not known locally; await replication`);
}
await engine.vote(proposalId, vote); Defensive patterns
Strategy: validation
Validate before calling
// guard with the engine-level lookup before voting
if (!engine.getProposal(proposalId)) {
// unknown locally: drop, or defer until replication delivers it
return;
}
await engine.vote(proposalId, vote); Try / catch
try {
await raft.vote(proposalId, vote);
} catch (e) {
if (e instanceof Error && /^Proposal .* not found$/.test(e.message)) {
// unknown proposal on this node: ignore stale votes or await replication
} else {
throw e;
}
} Prevention
- Only vote with ids returned by propose() — never reconstructed strings.
- On followers, expect proposals to arrive via replication before vote requests; drop unknown ids gracefully.
- After a restart, treat pre-restart proposal ids as gone — rebuild state instead of replaying votes.
When it happens
Trigger: Voting on a proposal id that was created on another node and never replicated here; a restart wiped the in-memory proposals map while vote messages kept arriving; a mistyped or truncated proposalId; using an engine-level id that differs from the raft-level id.
Common situations: Follower nodes receiving vote requests before the proposal replication reached them; replaying recorded messages after a process restart; string mismatches in ids passed between subsystems.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Only leader can propose values
- hexToBytes: odd-length hex string
- SSRF guard: only HTTPS URLs are permitted, got ${parsed.prot
- SSRF guard: private/loopback host rejected — ${host}
- SSRF guard: invalid URL — ${rawUrl}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/55fedac855eae96b.
Report an issue: GitHub.