ruvnet/ruflo · warning · Error
No contest to resolve
Error message
No contest to resolve
What it means
Thrown by resolveContest when the Claim exists but its contestInfo field is null/undefined. A contest must be opened first (producing a ContestInfo record on the claim) before anyone can resolve it. This guard runs after the not-found check, so it only fires for claims that exist but were never contested.
Source
Thrown at v3/@claude-flow/claims/src/application/work-stealing-service.ts:410
await this.emitContestEvent(claim);
}
// ===========================================================================
// Resolve Contest
// ===========================================================================
/**
* Resolve a contest (queen or human decides the winner)
*/
async resolveContest(issueId: IssueId, winner: Claimant, reason: string): Promise<void> {
const claim = await this.repository.findByIssueId(issueId);
if (!claim) {
throw new Error(`Claim not found for issue: ${issueId}`);
}
if (!claim.contestInfo) {
throw new Error('No contest to resolve');
}
if (claim.contestInfo.resolution) {
throw new Error('Contest has already been resolved');
}
const now = new Date();
const resolvedBy = this.determineResolver(winner, claim.contestInfo);
// Create resolution
const resolution: ContestResolution = {
resolvedAt: now,
winner,
resolvedBy,
reason,
};
claim.contestInfo.resolution = resolution;View on GitHub (pinned to 6b01dc5a68)
Solutions
- Before resolving, read the claim and confirm claim.contestInfo is present; if absent, treat the claim as uncontested and proceed with normal flow.
- Trace upstream: verify the contest-creation step (the call that attaches contestInfo) ran successfully and persisted before resolveContest.
- Gate resolveContest behind a helper like canResolveContest(claim) that checks both existence and contestInfo, so callers cannot reach the resolver without a contest.
- If contention is optional by design, consider a distinct API for uncontested claims rather than reusing resolveContest.
Example fix
// before
await service.resolveContest(issueId, winner, reason);
// after
const claim = await service.repository.findByIssueId(issueId);
if (claim && claim.contestInfo) {
await service.resolveContest(issueId, winner, reason);
} else {
logger.info({ issueId }, 'uncontested claim; nothing to resolve');
} Defensive patterns
Strategy: validation
Validate before calling
const claim = await service.repository.findByIssueId(issueId);
if (claim && !claim.contestInfo) {
// uncontested: take the non-contest path
return;
}
if (claim) await service.resolveContest(issueId, winner, reason); Type guard
function isContested(c: Claim | null | undefined): c is Claim & { contestInfo: ContestInfo } {
return !!c && !!c.contestInfo;
} Try / catch
try {
await service.resolveContest(issueId, winner, reason);
} catch (e) {
if (e instanceof Error && /No contest to resolve/.test(e.message)) return;
throw e;
} Prevention
- Only call resolveContest when a contest has actually been opened.
- Verify the contest-opening step succeeded and persisted before resolving.
- Encapsulate the resolve decision behind a canResolveContest(claim) helper.
- Distinguish uncontested claims from contested ones at the API level.
When it happens
Trigger: Calling resolveContest on a claim that is active/owned normally but has no competing claimant that triggered a contest. It happens when a caller treats any claim as resolvable rather than only contested ones, or when the contest-opening path was skipped or failed silently before resolution.
Common situations: Automation that always attempts to resolve after a timeout regardless of whether a contest occurred; a partial write where the claim was created but contestInfo was never attached due to an earlier exception; refactors that changed how contests are recorded without updating the resolver call sites.
Related errors
- Issue ${input.issueId} is not stealable or not claimed
- HANDOFF_PENDING
- INVALID_STATUS_TRANSITION
- Invalid target format: ${target}. Use agent:<id> or human:<i
- Issue not found: ${input.issueId}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/6393446ffaca886c.
Report an issue: GitHub.