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
- 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
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
- Always release with the claimant stored at claim time
- Keep agent ids stable across the claim lifecycle; re-registration should preserve ids
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
- Issue is not claimed by
- Issue is not claimed by
- Handoff not addressed to
- HANDOFF_PENDING
- Issue is not claimed by
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)