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

  1. Make release idempotent: check whether the claim exists before calling.
  2. Use the ClaimService events (issue:released, issue:stolen) to update local state so you do not call release on a stale view.
  3. 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

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


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/556d97e2364411e5. Report an issue: GitHub.