ruvnet/ruflo · error · Error
Issue ${input.issueId} is already claimed by ${issue.claimed
Error message
Issue ${input.issueId} is already claimed by ${issue.claimedBy} What it means
Thrown by the simple claimIssue handler right after the not-found check, when the issue exists but issue.claimedBy is already set to another claimant. The message names the current holder so the caller can decide to wait, request a handoff, or steal (if marked stealable).
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 6b01dc5a68)
Solutions
- Check issue.claimedBy before claiming, or call listAvailable to only see unclaimed issues.
- If the holder is stale, wait for expiration or use the steal flow (markStealable then steal).
- Use the service-backed path (context.claimsService.claim) which emits proper events and handles concurrency via the repository.
Example fix
// before
await claimIssue.handler({ issueId, claimantId: 'bob', claimantType: 'human' });
// throws 67 if issue.claimedBy === 'alice'
// after
const issue = await getIssue.handler({ issueId });
if (!issue.claimedBy) {
await claimIssue.handler({ issueId, claimantId: 'bob', claimantType: 'human' });
} else {
// request handoff, or wait, or steal via markStealable + steal
} Defensive patterns
Strategy: validation
Validate before calling
function isClaimable(issue) { return issue && !issue.claimedBy; }
if (!isClaimable(issueStore.get(input.issueId))) throw new Error('issue not claimable'); Type guard
function isClaimable(issue) { return !!issue && !issue.claimedBy; } Try / catch
null
Prevention
- Use listAvailable to enumerate only unclaimed issues.
- For contested issues, implement claim retries with backoff or use the steal flow.
When it happens
Trigger: Two callers racing to claim the same issue; an issue already assigned in a prior call within the same in-memory process; retrying claim after a timeout without checking state.
Common situations: Concurrent agents grabbing the same ticket; a developer re-running a claim script after it partially succeeded; an issue auto-assigned by a scheduler before manual claim.
Related errors
- Issue not found: ${input.issueId}
- Issue ${input.issueId} is not claimed by ${input.claimantId}
- No active claim found for issue ${input.issueId} by ${input.
- Issue ${input.issueId} is not stealable or not claimed
- browser/eval: script must not be empty
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/c38ca0c2e4fc71be.
Report an issue: GitHub.