ruvnet/ruflo · error

Issue is not claimed by

Error message

Issue ${issueId} is not claimed by ${this.formatClaimant(claimant)}

What it means

ClaimService.release(issueId, claimant) found a claim for the issue, but the stored claimant does not match the claimant argument (compared via isSameClaimant). Ownership is enforced on release so a random agent cannot free another agent's claim; only the holder (or an explicit steal/handoff flow) can change claim state. The message names the caller that attempted the release.

Solutions

  1. Release with the exact same Claimant (agentId + type) that acquired the claim — persist the claimant used at claim() time
  2. If a different agent must take over, use the steal/handoff flows (markStealable/steal, requestHandoff) instead of release()
  3. Inspect the stored claim via getIssueStatus(issueId).claimant to see who actually owns it

Example fix

// before
await claims.release(issueId, { agentId: 'coordinator', type: 'coordinator' }); // not the owner -> throws

// after
const claim = await claims.getIssueStatus(issueId);
if (!claim) return;
await claims.release(issueId, claim.claimant); // release as the recorded owner
Defensive patterns

Strategy: try-catch

Validate before calling

const claim = await claims.getIssueStatus(issueId);
if (claim && isSameClaimant(claim.claimant, me)) {
  await claims.release(issueId, me);
}

Try / catch

try {
  await claims.release(issueId, me);
} catch (e) {
  if (e instanceof Error && e.message.includes('is not claimed by')) {
    // not the owner: read claim.claimant via getIssueStatus and either act as that claimant or use the steal/handoff flow
  } else throw e;
}

Prevention

When it happens

Trigger: Releasing with a Claimant whose agentId/type differs from the one used in claim() — e.g. a restarted worker got a new agentId, a typo'd or default claimant, or a supervisor trying release() instead of the steal API.

Common situations: Agent identity not persisted across restarts so the 'same' worker returns with a different id, copy-pasted cleanup code using the coordinator's Claimant instead of the worker's, tests reusing one Claimant object for multiple roles.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/824cf393bfa60381. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/services/claim-service.ts:306

    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,
    });
  }

  // ==========================================================================
  // Handoffs
  // ==========================================================================

View on GitHub (pinned to fa13ee4ad6)