ruvnet/ruflo · error · Error

No active claim found for issue ${input.issueId} by ${input.

Error message

No active claim found for issue ${input.issueId} by ${input.claimantId}

What it means

Thrown by the simple updateStatus handler after it iterates claimStore.values() looking for an active claim matching both issueId and claimantId, and finds none. The handler updates progress/notes on a claim that must exist and belong to the caller; absence means either no claim was ever made or it belongs to someone else.

Source

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

  // Simple implementation
  for (const claim of claimStore.values()) {
    if (claim.issueId === input.issueId && claim.claimantId === input.claimantId) {
      claim.status = input.status;
      claim.lastActivityAt = new Date().toISOString();
      if (input.progress !== undefined) {
        claim.metadata = { ...claim.metadata, progress: input.progress };
      }
      return {
        issueId: input.issueId,
        status: input.status,
        progress: input.progress,
        updatedAt: claim.lastActivityAt,
        notes: input.notes,
      };
    }
  }

  throw new Error(`No active claim found for issue ${input.issueId} by ${input.claimantId}`);
}

/**
 * List unclaimed issues
 */
async function handleIssueListAvailable(
  input: z.infer<typeof issueListAvailableSchema>,
  context?: ToolContext
): Promise<{
  issues: Issue[];
  total: number;
  limit: number;
  offset: number;
}> {
  initializeMockData();

  if (context?.claimsService) {
    const result = await context.claimsService.listAvailableIssues(input);

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Ensure claimIssue succeeded for this issueId + claimantId before updateStatus.
  2. Re-claim if the previous claim was released or expired.
  3. List the claimant's active claims first and skip the update if none match.

Example fix

// before
await updateStatus.handler({ issueId, claimantId: 'alice', status: 'in-progress', progress: 50 }); // throws 70

// after
const claim = [...claimStore.values()].find(c => c.issueId === issueId && c.claimantId === 'alice' && c.status === 'active');
if (claim) await updateStatus.handler({ issueId, claimantId: 'alice', status: 'in-progress', progress: 50 });
Defensive patterns

Strategy: validation

Validate before calling

function hasActiveClaim(store, issueId, claimantId) {
  for (const c of store.values()) {
    if (c.issueId === issueId && c.claimantId === claimantId && c.status === 'active') return true;
  }
  return false;
}

Type guard

null

Try / catch

try { await updateStatus.handler(input, ctx); } catch (e) { if (/No active claim/.test(e.message)) { /* re-claim or skip */ } else throw e; }

Prevention

When it happens

Trigger: Calling updateStatus before claimIssue; calling it with a claimantId that does not own the claim; calling after the claim was released (status no longer active).

Common situations: Agent reporting progress on an issue it never claimed; status update racing with a release; stale claimantId after a restart.

Related errors


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