ruvnet/ruflo · error · Error

Issue ${input.issueId} is not stealable or not claimed

Error message

Issue ${input.issueId} is not stealable or not claimed

What it means

Thrown by the simple steal handler when no claim in claimStore matches the issue with claim.stealable === true. The steal flow requires the current holder to have first marked the claim stealable; otherwise the scan finds nothing and this fires.

Source

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

      // Update issue
      const issue = issueStore.get(input.issueId);
      if (issue) {
        issue.claimedBy = input.stealerId;
      }

      return {
        stolen: true,
        issueId: input.issueId,
        newClaimId,
        previousClaimant,
        contestWindowMs: 300000, // 5 minutes
        stolenAt,
      };
    }
  }

  throw new Error(`Issue ${input.issueId} is not stealable or not claimed`);
}

/**
 * Get stealable issues
 */
async function handleIssueGetStealable(
  input: z.infer<typeof issueGetStealableSchema>,
  context?: ToolContext
): Promise<{
  issues: Array<{
    issueId: string;
    title: string;
    priority: IssuePriority;
    currentClaimant: string;
    stealableReason?: string;
  }>;
  total: number;
}> {

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Call markStealable first (by the current holder), then steal.
  2. Re-check the issue's stealable state before stealing to handle races.
  3. If the issue is unclaimed, use claim directly rather than steal.

Example fix

// before
await steal.handler({ issueId, claimantId: 'bob' }); // throws 72 — not marked stealable

// after
await markStealable.handler({ issueId, claimantId: currentHolderId, reason: 'handoff' });
await steal.handler({ issueId, claimantId: 'bob' });
Defensive patterns

Strategy: validation

Validate before calling

function isStealable(store, issueId) {
  for (const c of store.values()) {
    if (c.issueId === issueId && c.stealable === true) return true;
  }
  return false;
}

Type guard

null

Try / catch

try { await steal.handler(input, ctx); } catch (e) { if (/not stealable/.test(e.message)) { /* mark or skip */ } else throw e; }

Prevention

When it happens

Trigger: Calling steal on an issue that was never marked stealable, on an unclaimed issue, or after the stealable flag was cleared.

Common situations: Skipping the markStealable step; the stealable window expired; another agent already stole it (clearing the flag); typo in issueId.

Related errors


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