{"record":{"id":"10229aad006d3add","repo":"ruvnet/ruflo","slug":"handoff-pending","errorCode":"HANDOFF_PENDING","errorMessage":"Cannot release claim with pending handoff to ${pendingHandoff.to.name}","messagePattern":"Cannot release claim with pending handoff to (.+?)","errorType":"exception","errorClass":"ClaimOperationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/claims/src/application/claim-service.ts","lineNumber":261,"sourceCode":"    const claim = await this.claimRepository.findByIssueId(issueId);\n\n    // Validate claim exists\n    if (!claim) {\n      throw new ClaimOperationError('NOT_CLAIMED', `Issue ${issueId} is not claimed`);\n    }\n\n    // Validate claimant owns the claim\n    if (claim.claimant.id !== claimant.id) {\n      throw new ClaimOperationError(\n        'UNAUTHORIZED',\n        `Claimant ${claimant.name} does not own the claim on issue ${issueId}`\n      );\n    }\n\n    // Check for pending handoffs\n    const pendingHandoff = claim.handoffChain?.find((h) => h.status === 'pending');\n    if (pendingHandoff) {\n      throw new ClaimOperationError(\n        'HANDOFF_PENDING',\n        `Cannot release claim with pending handoff to ${pendingHandoff.to.name}`\n      );\n    }\n\n    // Update claim status\n    const previousStatus = claim.status;\n    claim.status = 'released';\n    claim.lastActivityAt = new Date();\n\n    await this.claimRepository.save(claim);\n\n    // Emit events\n    const releaseEvent = createClaimReleasedEvent(claim.id, issueId, claimant);\n    await this.eventStore.append(releaseEvent);\n\n    if (previousStatus !== 'released') {\n      const statusEvent = createClaimStatusChangedEvent(","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/claims/src/application/claim-service.ts#L243-L279","documentation":"ClaimOperationError with code HANDOFF_PENDING, thrown by ClaimService.release when the claim's handoffChain contains a record with status 'pending'. A claim that is mid-handoff to another claimant cannot be released until that handoff is completed or cancelled, preventing the target claimant from receiving a dead claim.","triggerScenarios":"Calling release() after requestHandoff() but before the target accepts/rejects the handoff; orchestrators running release-all sweeps that hit claims in transfer; crashed handoff flows leaving a permanently pending record that then blocks release.","commonSituations":"Agent handoff at end of shift followed by immediate cleanup release; handoff target never responding so the pending record wedges the claim; automated teardown during a redeploy catching in-flight handoffs.","solutions":["Complete the pending handoff first: have the target claimant accept (or reject) it, then release or proceed","Cancel the pending handoff via the service's handoff cancellation path if the target is unreachable, then release","Order shutdown flows as: resolve handoffs -> release claims, and skip release for claims with pending handoffs"],"exampleFix":"// before\nawait claimService.requestHandoff(issueId, from, to, 'shift end');\nawait claimService.release(issueId, from); // throws HANDOFF_PENDING\n\n// after\nawait claimService.requestHandoff(issueId, from, to, 'shift end');\nawait claimService.acceptHandoff(issueId, to); // or rejectHandoff to cancel\nawait claimService.release(issueId, from); // handoff no longer pending","handlingStrategy":"try-catch","validationCode":"const claim = await claimRepository.findByIssueId(issueId);\nconst pending = claim?.handoffChain?.find(h => h.status === 'pending');\nif (pending) {\n  await claimService.cancelHandoff(issueId, pending); // or wait for accept/reject\n}\nawait claimService.release(issueId, claimant);","typeGuard":null,"tryCatchPattern":"try { await claimService.release(issueId, claimant); } catch (e) { if (e instanceof ClaimOperationError && e.code === 'HANDOFF_PENDING') { await resolveOrCancelPendingHandoff(issueId); return claimService.release(issueId, claimant); } throw e; }","preventionTips":["Order teardown: resolve handoffs before releasing claims","Add timeouts to handoff acceptance so pending records cannot wedge releases"],"tags":["claims","handoff","release","invalid-state"],"backgroundTag":"precondition-failed","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"}