ruvnet/ruflo · warning
Proposal ${proposalId} already resolved
Error message
Proposal ${proposalId} already resolved What it means
AdversarialCoordinator.vote() refuses votes on proposals already marked `resolved` (set by resolve()). This keeps the final quorum decision immutable after resolution. The check runs after the existence check, so seeing this error means the ID is valid but voting is closed.
Source
Thrown at v3/@claude-flow/guidance/src/adversarial.ts:699
}
if (oldestId) {
this.proposals.delete(oldestId);
}
}
return proposalId;
}
/**
* Vote on a proposal
*/
vote(proposalId: string, voterId: string, approve: boolean): void {
const proposal = this.proposals.get(proposalId);
if (!proposal) {
throw new Error(`Proposal ${proposalId} not found`);
}
if (proposal.resolved) {
throw new Error(`Proposal ${proposalId} already resolved`);
}
proposal.votes.set(voterId, approve);
}
/**
* Resolve a proposal (check if quorum reached)
*/
resolve(proposalId: string): QuorumResult {
const proposal = this.proposals.get(proposalId);
if (!proposal) {
throw new Error(`Proposal ${proposalId} not found`);
}
// Single pass over votes instead of two filter calls
let forCount = 0;
let againstCount = 0;
for (const v of proposal.votes.values()) {View on GitHub (pinned to fa13ee4ad6)
Solutions
- Check `coordinator.getProposal(id)?.resolved` before voting and skip closed proposals
- Treat this error as a no-op signal in retry/replay logic (catch and drop the duplicate vote)
- Propose a new proposal if a post-resolution vote genuinely must be recorded
Example fix
// before
coordinator.vote(proposalId, voterId, true); // may throw 'already resolved'
// after
const p = coordinator.getProposal(proposalId);
if (p && !p.resolved) {
coordinator.vote(proposalId, voterId, true);
} else {
// voting closed; duplicate or late vote — drop it
} Defensive patterns
Strategy: validation
Validate before calling
const p = coordinator.getProposal(proposalId);
if (p && !p.resolved) {
coordinator.vote(proposalId, voterId, approve);
} Try / catch
try {
coordinator.vote(proposalId, voterId, approve);
} catch (err) {
if (err instanceof Error && err.message.includes('already resolved')) {
// late/duplicate vote after quorum — safe to ignore
} else throw err;
} Prevention
- Make voter pipelines idempotent: check `resolved` before voting
- Drop replayed votes in at-least-once delivery systems instead of retrying them
- Create a fresh proposal per test case
When it happens
Trigger: Calling vote() after resolve(proposalId) already ran; two voters racing where one voter triggers resolution and the second vote lands after; retry pipelines that replay all votes including ones already counted toward a resolved proposal.
Common situations: Fan-out voting where late voters straggle in after quorum was reached; event replay / at-least-once delivery re-delivering a vote; test suites that reuse a proposal across cases without recreating it.
Related errors
- Proposal ${proposalId} not found
- Proposal not found: ${proposalId}
- GuidanceControlPlane not initialized. Call initialize() firs
- No policy bundle loaded. Call loadBundle() first.
- SSRF guard: invalid URL — ${rawUrl}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/98517b6f9a2d458f.
Report an issue: GitHub.