{"record":{"id":"2e0326b1669b2a7b","repo":"ruvnet/ruflo","slug":"not-claimed","errorCode":"NOT_CLAIMED","errorMessage":"Issue ${issueId} is not claimed","messagePattern":"Issue (.+?) is not claimed","errorType":"error_code","errorClass":"ClaimOperationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/claims/src/application/claim-service.ts","lineNumber":247,"sourceCode":"      reviewers: [],\n    };\n\n    // Save claim\n    await this.claimRepository.save(claim);\n\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    }","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/claims/src/application/claim-service.ts#L229-L265","documentation":"Thrown by ClaimService.release when claimRepository.findByIssueId(issueId) returns null/undefined. This is the application-layer equivalent of the MCP 'Issue not found' on release, but in the DDD service. Code is NOT_CLAIMED, distinguishing 'nothing to release' from 'wrong owner'.","triggerScenarios":"Calling service.release on an issueId that has never been claimed, whose claim was already released, or that lives in a different repository instance.","commonSituations":"Idempotent release retry after success; pointing at the wrong repository (in-memory vs persisted); claim TTL expired and was purged.","solutions":["Make release idempotent: catch NOT_CLAIMED and treat as success if already-released is acceptable.","Confirm the claim exists via findByIssueId before calling release.","Ensure the same repository instance is used across claim and release."],"exampleFix":"// before\nawait service.release(issueId, claimant); // throws NOT_CLAIMED\n\n// after\ntry {\n  await service.release(issueId, claimant);\n} catch (e) {\n  if (e instanceof ClaimOperationError && e.code === 'NOT_CLAIMED') return; // already released\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"const claim = await claimRepo.findByIssueId(issueId);\nif (!claim) return; // nothing to release","typeGuard":"null","tryCatchPattern":"try { await service.release(issueId, claimant); } catch (e) {\n  if (e instanceof ClaimOperationError && e.code === 'NOT_CLAIMED') return;\n  throw e;\n}","preventionTips":["Make release idempotent by swallowing NOT_CLAIMED when that fits your semantics.","Use the same repository instance across claim and release."],"tags":["claims","ddd","service-layer","not-claimed","typescript"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}