ruvnet/ruflo · error · ClaimOperationError

NOT_CLAIMED

NOT_CLAIMED

Error message

Issue ${issueId} is not claimed

What it means

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'.

Source

Thrown at v3/@claude-flow/claims/src/application/claim-service.ts:247

      reviewers: [],
    };

    // Save claim
    await this.claimRepository.save(claim);

    // Emit event
    const event = createClaimCreatedEvent(claimId, issueId, claimant);
    await this.eventStore.append(event);

    return { success: true, claim };
  }

  async release(issueId: string, claimant: Claimant): Promise<void> {
    const claim = await this.claimRepository.findByIssueId(issueId);

    // Validate claim exists
    if (!claim) {
      throw new ClaimOperationError('NOT_CLAIMED', `Issue ${issueId} is not claimed`);
    }

    // Validate claimant owns the claim
    if (claim.claimant.id !== claimant.id) {
      throw new ClaimOperationError(
        'UNAUTHORIZED',
        `Claimant ${claimant.name} does not own the claim on issue ${issueId}`
      );
    }

    // Check for pending handoffs
    const pendingHandoff = claim.handoffChain?.find((h) => h.status === 'pending');
    if (pendingHandoff) {
      throw new ClaimOperationError(
        'HANDOFF_PENDING',
        `Cannot release claim with pending handoff to ${pendingHandoff.to.name}`
      );
    }

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Make release idempotent: catch NOT_CLAIMED and treat as success if already-released is acceptable.
  2. Confirm the claim exists via findByIssueId before calling release.
  3. Ensure the same repository instance is used across claim and release.

Example fix

// before
await service.release(issueId, claimant); // throws NOT_CLAIMED

// after
try {
  await service.release(issueId, claimant);
} catch (e) {
  if (e instanceof ClaimOperationError && e.code === 'NOT_CLAIMED') return; // already released
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const claim = await claimRepo.findByIssueId(issueId);
if (!claim) return; // nothing to release

Type guard

null

Try / catch

try { await service.release(issueId, claimant); } catch (e) {
  if (e instanceof ClaimOperationError && e.code === 'NOT_CLAIMED') return;
  throw e;
}

Prevention

When it happens

Trigger: Calling service.release on an issueId that has never been claimed, whose claim was already released, or that lives in a different repository instance.

Common situations: Idempotent release retry after success; pointing at the wrong repository (in-memory vs persisted); claim TTL expired and was purged.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/2e0326b1669b2a7b. Report an issue: GitHub.