{"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":"Error","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/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/claim-service.ts#L319-L355","documentation":"Second guard in requestHandoff(): the claim exists but its claimant is not isSameClaimant to `from`. The message names the caller's `from` claimant. This prevents an agent from initiating a handoff on behalf of a different (possibly unaware) claimant. Status is not changed to handoff-pending.","triggerScenarios":"Agent B calling requestHandoff with from=agentA on an issue held by agentB (or vice versa); a coordinator passing the wrong `from`; stale claimant identity after an agent rename.","commonSituations":"Coordinator-driven handoffs that guess the current holder; mixed human/agent workflows where the `from` type is wrong; copy-paste of a claimant object between calls.","solutions":["Look up the actual current claimant and pass it as `from`.","Restrict requestHandoff to the holder calling it itself.","Use stable identity fields (agentId/userId) consistently across claim, release, and handoff."],"exampleFix":"// before: coordinator guesses `from`\nawait claims.requestHandoff(issueId, assumedFrom, to, reason); // throws\n\n// after: derive from from current claim\nconst claim = claims.list?.().find(c => c.issueId === issueId);\nif (!claim) return;\nawait claims.requestHandoff(issueId, claim.claimant, to, reason);","handlingStrategy":"validation","validationCode":"function handoffFromCurrentHolder(claims, issueId, to, reason) {\n  const c = claims.list?.().find(x => x.issueId === issueId);\n  if (!c) throw new Error('no claim to hand off');\n  return claims.requestHandoff(issueId, c.claimant, to, reason);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass the actual current claimant as `from`, not an assumed one.","Restrict requestHandoff to the holder calling it itself when possible.","Keep identity (agentId/userId) consistent across claim/release/handoff."],"tags":["claim-service","handoff","ownership","authorization"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}