{"record":{"id":"6393446ffaca886c","repo":"ruvnet/ruflo","slug":"no-contest-to-resolve","errorCode":null,"errorMessage":"No contest to resolve","messagePattern":"No contest to resolve","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/claims/src/application/work-stealing-service.ts","lineNumber":410,"sourceCode":"    await this.emitContestEvent(claim);\n  }\n\n  // ===========================================================================\n  // Resolve Contest\n  // ===========================================================================\n\n  /**\n   * Resolve a contest (queen or human decides the winner)\n   */\n  async resolveContest(issueId: IssueId, winner: Claimant, reason: string): Promise<void> {\n    const claim = await this.repository.findByIssueId(issueId);\n\n    if (!claim) {\n      throw new Error(`Claim not found for issue: ${issueId}`);\n    }\n\n    if (!claim.contestInfo) {\n      throw new Error('No contest to resolve');\n    }\n\n    if (claim.contestInfo.resolution) {\n      throw new Error('Contest has already been resolved');\n    }\n\n    const now = new Date();\n    const resolvedBy = this.determineResolver(winner, claim.contestInfo);\n\n    // Create resolution\n    const resolution: ContestResolution = {\n      resolvedAt: now,\n      winner,\n      resolvedBy,\n      reason,\n    };\n\n    claim.contestInfo.resolution = resolution;","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/claims/src/application/work-stealing-service.ts#L392-L428","documentation":"Thrown by resolveContest when the claim exists but claim.contestInfo is undefined — no steal contest was ever opened for it. resolveContest only applies to claims whose steal produced contestInfo (a contestedBy record, window, and future resolution); calling it on a normally-held or never-stolen claim is an invalid state transition.","triggerScenarios":"resolveContest invoked before anyone called contestSteal (or before a steal created contestInfo); resolving a claim that was acquired without stealing; a claim row recreated after a previous contest finished, wiping contestInfo.","commonSituations":"Event-ordering races where a resolution command arrives before the steal/contest event; admin UIs that offer 'resolve' on every claim; replaying recorded resolution events against a fresh repository that never saw the steal.","solutions":["Guard the call: read the claim and require claim.contestInfo && !claim.contestInfo.resolution before resolving","Fix orchestration ordering so resolveContest only fires after contestSteal has succeeded","When replaying events, rebuild full claim state (including the steal) before applying resolution events","Treat the missing-contest case as an idempotent no-op if your delivery layer may reorder"],"exampleFix":"// before\nawait svc.resolveContest(issueId, winner, reason);\n\n// after\nconst claim = await svc.repository.findByIssueId(issueId);\nif (claim?.contestInfo && !claim.contestInfo.resolution) {\n  await svc.resolveContest(issueId, winner, reason);\n}","handlingStrategy":"try-catch","validationCode":"const claim = await repository.findByIssueId(issueId);\nif (claim?.contestInfo && !claim.contestInfo.resolution) {\n  await svc.resolveContest(issueId, winner, reason);\n}","typeGuard":"function hasOpenContest(claim: IssueClaim | null): boolean {\n  return !!claim?.contestInfo && !claim.contestInfo.resolution;\n}","tryCatchPattern":"try { await svc.resolveContest(issueId, winner, reason); }\ncatch (e) { if (e instanceof Error && e.message === 'No contest to resolve') { /* ordering race: re-deliver later or no-op */ } else throw e; }","preventionTips":["Sequence orchestration so resolve only fires after contestSteal commits","When replaying event logs, apply events in causal order (use the recorded timestamps)","Expose contest state in the UI so 'resolve' is only offered for contested claims"],"tags":["claims","invalid-state","work-stealing","typescript"],"backgroundTag":"invalid-state-transition","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}