ruvnet/ruflo · error · Error
Issue ${issueId} is not claimed
Error message
Issue ${issueId} is not claimed What it means
ClaimService.release(issueId, claimant) looks up the claim in its in-memory map; if none exists for that issueId it throws 'Issue ... is not claimed' before checking the claimant. This is the first of two guards in release: existence, then ownership. There is no 'release if exists' variant — callers must know the claim is present.
Source
Thrown at v3/@claude-flow/cli/src/services/claim-service.ts:302
this.claims.set(issueId, claim);
await this.saveClaims();
this.emitEvent({
type: 'issue:claimed',
timestamp: now,
issueId,
claimant,
previousClaimant: existing?.claimant,
});
return { success: true, claim };
}
async release(issueId: string, claimant: Claimant): Promise<void> {
const claim = this.claims.get(issueId);
if (!claim) {
throw new Error(`Issue ${issueId} is not claimed`);
}
if (!this.isSameClaimant(claim.claimant, claimant)) {
throw new Error(`Issue ${issueId} is not claimed by ${this.formatClaimant(claimant)}`);
}
this.claims.delete(issueId);
this.stealableInfo.delete(issueId);
await this.saveClaims();
this.emitEvent({
type: 'issue:released',
timestamp: new Date(),
issueId,
claimant,
});
}
View on GitHub (pinned to 6b01dc5a68)
Solutions
- Make release idempotent: check whether the claim exists before calling.
- Use the ClaimService events (issue:released, issue:stolen) to update local state so you do not call release on a stale view.
- Tolerate 'not claimed' as a success in retry paths (it is already in the desired state).
Example fix
// before
await claims.release(issueId, claimant); // throws if already released
// after: tolerate already-released as success
try {
await claims.release(issueId, claimant);
} catch (e) {
if (!String(e.message).includes('is not claimed')) throw e;
// already in desired state — no-op
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await claims.release(issueId, claimant);
} catch (e) {
if (String(e.message).includes('is not claimed')) return; // already released — desired state
throw e;
} Prevention
- Treat 'is not claimed' on release as idempotent success in retry paths.
- Drive local claim state from ClaimService events (issue:released, issue:stolen) to avoid stale releases.
- Avoid double-release by tracking a released flag per issue in the caller.
When it happens
Trigger: Calling release() for an issue that was never claimed, that was already released, or whose claim expired and was reaped; double-release by two code paths; releasing after a steal that already removed the old claimant's entry.
Common situations: Agent crash-recovery retrying release on an issue another agent already stole and released; idempotency logic that re-runs release; UI 'unclaim' button pressed twice.
Related errors
- No pending handoff for issue ${issueId}
- Issue ${issueId} is not claimed by ${this.formatClaimant(cla
- Issue ${input.issueId} is already claimed by ${issue.claimed
- Issue ${input.issueId} is not stealable or not claimed
- HANDOFF_PENDING
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/556d97e2364411e5.
Report an issue: GitHub.