ruvnet/ruflo · error · Error
Issue ${issueId} is not claimed by ${this.formatClaimant(cla
Error message
Issue ${issueId} is not claimed by ${this.formatClaimant(claimant)} What it means
The second guard in release(): the claim exists, but isSameClaimant(claim.claimant, claimant) is false. isSameClaimant compares type plus userId (human) or agentId (agent). The message names the caller's claimant via formatClaimant (human:<name> or agent:<type>:<id>). The claim is not removed.
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 6b01dc5a68)
Solutions
- Have each caller release only with the exact claimant it used to claim.
- If a transfer is intended, use the handoff flow (requestHandoff/acceptHandoff) rather than release+claim.
- Resolve identity drift: derive agentId from a stable source (not a random per-run id).
Example fix
// before: agent A releases B's claim
await claims.release(issueId, agentA); // claim held by agentB -> throws
// after: only the holder releases, or use handoff
const claim = claims.peek?.(issueId);
if (claim && isSameClaimant(claim.claimant, agentA)) {
await claims.release(issueId, agentA);
} else if (claim) {
await claims.requestHandoff(issueId, claim.claimant, agentA, 'rebalance');
} Defensive patterns
Strategy: validation
Validate before calling
function canRelease(claims, issueId, claimant) {
const c = claims.list?.().find(x => x.issueId === issueId);
return !!c && isSameClaimant(c.claimant, claimant);
} Type guard
function isSameClaimant(a, b): boolean {
if (a.type !== b.type) return false;
return a.type === 'human' ? a.userId === b.userId : a.agentId === b.agentId;
} Prevention
- Always release with the exact claimant used to claim.
- Use the handoff flow rather than release+claim for transfers.
- Derive agentId/userId from stable sources, not per-run random ids.
When it happens
Trigger: Agent A trying to release a claim held by agent B; a human trying to release an agent's claim; using a stale claimant object whose agentId changed after a restart; case/whitespace drift in userId.
Common situations: Two agents with overlapping scopes; recovery logic using the wrong identity; tests using a default claimant that does not match the one used in claim(); renaming an agent without updating persisted claimant refs.
Related errors
- Issue ${issueId} is not claimed by ${this.formatClaimant(fro
- Handoff not addressed to ${this.formatClaimant(claimant)}
- Issue ${input.issueId} is not claimed by ${input.claimantId}
- UNAUTHORIZED
- Issue ${issueId} is not claimed
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/824cf393bfa60381.
Report an issue: GitHub.