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

  1. Pre-check with the public API: const claim = await service.getIssueStatus(issueId); if (claim) await service.release(...)
  2. Treat release as idempotent: catch this specific error and ignore it when the end state (unclaimed) is already achieved
  3. 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

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


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)