{"record":{"id":"08e3c2638f0e5eab","repo":"ruvnet/ruflo","slug":"issue-issueid-is-not-claimed-by-this-formatcl-08e3c2","errorCode":null,"errorMessage":"Issue ${issueId} is not claimed by ${this.formatClaimant(from)}","messagePattern":"Issue (.+?) is not claimed by (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/claim-service.ts","lineNumber":337,"sourceCode":"  }\n\n  // ==========================================================================\n  // Handoffs\n  // ==========================================================================\n\n  async requestHandoff(\n    issueId: string,\n    from: Claimant,\n    to: Claimant,\n    reason: string\n  ): Promise<void> {\n    const claim = this.claims.get(issueId);\n    if (!claim) {\n      throw new Error(`Issue ${issueId} is not claimed`);\n    }\n\n    if (!this.isSameClaimant(claim.claimant, from)) {\n      throw new Error(`Issue ${issueId} is not claimed by ${this.formatClaimant(from)}`);\n    }\n\n    claim.status = 'handoff-pending';\n    claim.statusChangedAt = new Date();\n    claim.handoffTo = to;\n    claim.handoffReason = reason;\n    await this.saveClaims();\n\n    this.emitEvent({\n      type: 'issue:handoff:requested',\n      timestamp: new Date(),\n      issueId,\n      claimant: from,\n      data: { to, reason },\n    });\n  }\n\n  async acceptHandoff(issueId: string, claimant: Claimant): Promise<void> {","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/services/claim-service.ts#L319-L355","documentation":"ClaimService.requestHandoff(issueId, from, to, reason) found a claim for the issue, but the stored claimant does not match the from argument (isSameClaimant fails). Only the current owner can initiate a handoff — this prevents an agent from volunteering someone else's work. The message names the from claimant that was rejected.","triggerScenarios":"Passing a supervisor/coordinator Claimant as from when a worker actually holds the claim, using a stale agentId after the worker restarted with a new identity, or calling requestHandoff with swapped from/to arguments.","commonSituations":"Coordinator-driven reassignment code that assumes it can hand off on behalf of workers, agent ids regenerated per session, argument-order mistakes in the four-parameter call.","solutions":["Read the current owner first (getIssueStatus(issueId).claimant) and pass that as from","Have the owning worker itself call requestHandoff rather than a third party","Stabilize agent identity (persistent agentId) so restarts do not change ownership mid-flight"],"exampleFix":"// before\nawait claims.requestHandoff(issueId, coordinator, workerB, 'rebalance'); // coordinator is not the owner\n\n// after\nconst claim = await claims.getIssueStatus(issueId);\nif (!claim) throw new Error('nothing to hand off');\nawait claims.requestHandoff(issueId, claim.claimant, workerB, 'rebalance');","handlingStrategy":"try-catch","validationCode":"const claim = await claims.getIssueStatus(issueId);\nif (!claim) throw new Error('nothing to hand off');\nawait claims.requestHandoff(issueId, claim.claimant, to, reason); // from = actual owner","typeGuard":null,"tryCatchPattern":"try {\n  await claims.requestHandoff(issueId, from, to, reason);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('is not claimed by')) {\n    const c = await claims.getIssueStatus(issueId);\n    if (c) await claims.requestHandoff(issueId, c.claimant, to, reason); // retry as the real owner\n    else throw e;\n  } else throw e;\n}","preventionTips":["Always read the current owner (getIssueStatus) instead of assuming who holds the claim","Let the owning worker initiate its own handoff; coordinators should request, not forge, the from identity","Keep agent identity durable across restarts to keep isSameClaimant meaningful"],"tags":["claim-service","handoff","ownership","authorization"],"backgroundTag":"ownership-mismatch","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"}