ruvnet/ruflo · error · ClaimOperationError

UNAUTHORIZED

UNAUTHORIZED

Error message

Claimant ${claimant.name} does not own the claim on issue ${issueId}

What it means

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.

Solutions

  1. Fetch the claim and release using the claimant recorded at claim time (claim.claimant)
  2. If supervision must force-release, extend the service with an admin/override path rather than spoofing an owner id
  3. After handoffs, ensure only the new owner releases; stale owners should stand down

Example fix

// before
await claimService.release(issueId, orchestratorClaimant); // worker holds claim

// after
const claim = await claimRepository.findByIssueId(issueId);
if (claim.claimant.id !== claimant.id) {
  throw new Error(`refusing to release claim owned by ${claim.claimant.id}`);
}
await claimService.release(issueId, claim.claimant);
Defensive patterns

Strategy: try-catch

Validate before calling

const claim = await claimRepository.findByIssueId(issueId);
if (claim && claim.claimant.id !== claimant.id) {
  throw new Error(`refusing release: claim owned by ${claim.claimant.id}`);
}
await claimService.release(issueId, claimant);

Try / catch

try { await claimService.release(issueId, claimant); } catch (e) { if (e instanceof ClaimOperationError && e.code === 'UNAUTHORIZED') { /* fetch real owner, escalate or hand off */ } throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/bbc1fbed5cfd371d. Report an issue: GitHub.

Appendix: source

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

    // 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}`
      );
    }

    // Update claim status
    const previousStatus = claim.status;
    claim.status = 'released';
    claim.lastActivityAt = new Date();

View on GitHub (pinned to fa13ee4ad6)