{"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":"Error","httpStatus":null,"severity":"warning","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/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/claims/src/application/work-stealing-service.ts#L392-L428","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait service.resolveContest(issueId, winner, reason);\n\n// after\nconst claim = await service.repository.findByIssueId(issueId);\nif (claim && claim.contestInfo) {\n  await service.resolveContest(issueId, winner, reason);\n} else {\n  logger.info({ issueId }, 'uncontested claim; nothing to resolve');\n}","handlingStrategy":"validation","validationCode":"const claim = await service.repository.findByIssueId(issueId);\nif (claim && !claim.contestInfo) {\n  // uncontested: take the non-contest path\n  return;\n}\nif (claim) await service.resolveContest(issueId, winner, reason);","typeGuard":"function isContested(c: Claim | null | undefined): c is Claim & { contestInfo: ContestInfo } {\n  return !!c && !!c.contestInfo;\n}","tryCatchPattern":"try {\n  await service.resolveContest(issueId, winner, reason);\n} catch (e) {\n  if (e instanceof Error && /No contest to resolve/.test(e.message)) return;\n  throw e;\n}","preventionTips":["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."],"tags":["claims","domain-logic","state-machine","contest"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}