ruvnet/ruflo · error

Issue is already claimed by

Error message

Issue ${input.issueId} is already claimed by ${issue.claimedBy}

What it means

Thrown by the claim-issue MCP tool handler in @claude-flow/claims when the issue exists but issue.claimedBy is already set, i.e. another claimant holds an active claim. This is the double-claim guard in the 'Simple implementation' path and fires before a new Claim record is created.

Solutions

  1. Have the current holder call the release tool (or release with the owner's claimantId) before re-claiming
  2. If the holder is defunct, use the mark-stealable + steal flow (issue_mark_stealable then issue_steal) to take over the claim
  3. Query the issue state first and skip claiming when claimedBy is set, treating the issue as taken
  4. Make claim attempts idempotent per agent: if already claimed by the same claimantId, proceed instead of erroring

Example fix

// before
await claimTool({ issueId, claimantId: 'agent-2', claimantType: 'agent' }); // agent-1 holds it

// after
const issue = await getIssueTool({ issueId });
if (issue.claimedBy && issue.claimedBy !== 'agent-2') {
  await markStealableTool({ issueId, claimantId: issue.claimedBy, reason: 'owner unresponsive' });
  await stealTool({ issueId, claimantId: 'agent-2', claimantType: 'agent' });
} else {
  await claimTool({ issueId, claimantId: 'agent-2', claimantType: 'agent' });
}
Defensive patterns

Strategy: validation

Validate before calling

const issue = issueStoreSnapshot.get(issueId); // via get-issue tool
if (issue?.claimedBy && issue.claimedBy !== claimantId) {
  return { skipped: true, reason: `taken by ${issue.claimedBy}` };
}
await claimTool({ issueId, claimantId, claimantType });

Try / catch

try { await claimTool(input); } catch (e) { if (e instanceof Error && e.message.includes('is already claimed by')) { const holder = e.message.split('claimed by ')[1]; await coordinateWith(holder); } throw e; }

Prevention

When it happens

Trigger: Two agents calling the claim tool for the same issueId concurrently or sequentially while the first claim is still active; retrying a claim after a network timeout when the first attempt actually succeeded; claiming an issue whose prior owner never called release.

Common situations: Multiple swarm workers scanning the same backlog and racing for tasks; at-least-once retry logic in the client duplicating a successful claim; stale claims from crashed agents that never released, permanently blocking the issue.

Related errors


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

Appendix: source

Thrown at v3/@claude-flow/claims/src/api/mcp-tools.ts:503

    return {
      claimId: claim.id,
      issueId: claim.issueId,
      claimantId: claim.claimantId,
      claimantType: claim.claimantType,
      status: claim.status,
      claimedAt: claim.claimedAt,
      expiresAt: claim.expiresAt,
    };
  }

  // Simple implementation
  const issue = issueStore.get(input.issueId);
  if (!issue) {
    throw new Error(`Issue not found: ${input.issueId}`);
  }

  if (issue.claimedBy) {
    throw new Error(`Issue ${input.issueId} is already claimed by ${issue.claimedBy}`);
  }

  const claimId = generateSecureId('claim');
  const claimedAt = new Date().toISOString();
  const expiresAt = input.expiresInMs
    ? new Date(Date.now() + input.expiresInMs).toISOString()
    : undefined;

  const claim: Claim = {
    id: claimId,
    issueId: input.issueId,
    claimantType: input.claimantType,
    claimantId: input.claimantId,
    status: 'active',
    priority: input.priority || issue.priority,
    stealable: false,
    claimedAt,
    lastActivityAt: claimedAt,

View on GitHub (pinned to fa13ee4ad6)