ruvnet/ruflo · error
Issue is not claimed
Error message
Issue ${issueId} is not claimed What it means
ClaimService.release(issueId, claimant) looks up the issue in the in-memory claims map and throws this when no claim exists for issueId at all. There is nothing to release: either the issue was never claimed, the claim was already released, or it moved (stolen / handed off) and this service instance has not observed it. The claim-ownership mismatch case is a separate follow-up error ('not claimed by ...').
Solutions
- Pre-check with the public API: const claim = await service.getIssueStatus(issueId); if (claim) await service.release(...)
- Treat release as idempotent: catch this specific error and ignore it when the end state (unclaimed) is already achieved
- If state seems wrong, re-sync from persisted claims (getAllClaims()/event log) before acting
Example fix
// before await claims.release(issueId, me); // in a finally block -> throws on second pass // after const existing = await claims.getIssueStatus(issueId); if (existing) await claims.release(issueId, me);
Defensive patterns
Strategy: try-catch
Validate before calling
const claim = await claims.getIssueStatus(issueId); if (claim) await claims.release(issueId, claimant);
Try / catch
try {
await claims.release(issueId, claimant);
} catch (e) {
if (e instanceof Error && e.message.endsWith('is not claimed')) return; // already unclaimed — idempotent success
throw e;
} Prevention
- Guard release paths with getIssueStatus(issueId) so double-cleanup is a no-op
- Release exactly once per claim acquisition, from the finally block that owns it
- When state surprises you, consult getEventLog() to see the release/steal that got there first
When it happens
Trigger: Calling release() twice for the same issue (double release in cleanup code), releasing after the claim was already handed off or completed, releasing an issue id that was never acquired, or using a ClaimService instance whose loaded state is stale relative to the process that holds the claim.
Common situations: Idempotent cleanup paths (finally blocks) that release on every exit including retries, two workers racing to release the same issue, CI runs where teardown re-executes, long-lived processes whose claims file was modified by another process.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Issue is not claimed by
- No pending handoff for issue
- Can only assign queued or pending tasks
- Can only complete running tasks
- Can only fail running or assigned tasks
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/556d97e2364411e5.
Report an issue: GitHub.
Appendix: 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 fa13ee4ad6)