{"record":{"id":"bbc1fbed5cfd371d","repo":"ruvnet/ruflo","slug":"unauthorized-bbc1fb","errorCode":"UNAUTHORIZED","errorMessage":"Claimant ${claimant.name} does not own the claim on issue ${issueId}","messagePattern":"Claimant (.+?) does not own the claim on issue (.+?)","errorType":"exception","errorClass":"ClaimOperationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/claims/src/application/claim-service.ts","lineNumber":252,"sourceCode":"\n    // Emit event\n    const event = createClaimCreatedEvent(claimId, issueId, claimant);\n    await this.eventStore.append(event);\n\n    return { success: true, claim };\n  }\n\n  async release(issueId: string, claimant: Claimant): Promise<void> {\n    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();","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/claims/src/application/claim-service.ts#L234-L270","documentation":"ClaimOperationError with code UNAUTHORIZED, thrown by ClaimService.release when a claim exists but claim.claimant.id !== claimant.id — the caller is not the claim owner. The service enforces owner-only release as the second validation after existence, before any handoff checks or status mutation.","triggerScenarios":"An orchestrator/supervisor calling release() with its own claimant while a worker holds the claim; id drift such as releasing with 'agent-coder-1' when the claim was created with 'coder-1'; attempting release after a handoff transferred ownership to another claimant.","commonSituations":"Centralized shutdown code releasing all claims under one identity; agent rename/re-registration changing ids; double-bookkeeping where two systems track different owner ids for the same issue.","solutions":["Fetch the claim and release using the claimant recorded at claim time (claim.claimant)","If supervision must force-release, extend the service with an admin/override path rather than spoofing an owner id","After handoffs, ensure only the new owner releases; stale owners should stand down"],"exampleFix":"// before\nawait claimService.release(issueId, orchestratorClaimant); // worker holds claim\n\n// after\nconst claim = await claimRepository.findByIssueId(issueId);\nif (claim.claimant.id !== claimant.id) {\n  throw new Error(`refusing to release claim owned by ${claim.claimant.id}`);\n}\nawait claimService.release(issueId, claim.claimant);","handlingStrategy":"try-catch","validationCode":"const claim = await claimRepository.findByIssueId(issueId);\nif (claim && claim.claimant.id !== claimant.id) {\n  throw new Error(`refusing release: claim owned by ${claim.claimant.id}`);\n}\nawait claimService.release(issueId, claimant);","typeGuard":null,"tryCatchPattern":"try { await claimService.release(issueId, claimant); } catch (e) { if (e instanceof ClaimOperationError && e.code === 'UNAUTHORIZED') { /* fetch real owner, escalate or hand off */ } throw e; }","preventionTips":["Always release with the claimant stored at claim time","Keep agent ids stable across the claim lifecycle; re-registration should preserve ids"],"tags":["claims","authorization","release","ownership"],"backgroundTag":"authorization-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"}