ruvnet/ruflo · warning · ClaimOperationError

MAX_CLAIMS_EXCEEDED

MAX_CLAIMS_EXCEEDED

Error message

Cannot accept handoff: claimant ${claimant.name} at max capacity

What it means

Thrown by acceptHandoff after the claim and pending handoff are validated, when the accepting claimant's active claim count (claimRepository.countByClaimant) is greater than or equal to claimant.maxConcurrentClaims, defaulting to 5 when unset. This is the capacity guard that prevents an agent from over-committing.

Source

Thrown at v3/@claude-flow/claims/src/application/claim-service.ts:380

    }

    // Find pending handoff for this claimant
    const pendingHandoff = claim.handoffChain?.find(
      (h) => h.status === 'pending' && h.to.id === claimant.id
    );

    if (!pendingHandoff) {
      throw new ClaimOperationError(
        'HANDOFF_NOT_FOUND',
        `No pending handoff found for claimant ${claimant.name}`
      );
    }

    // Check claimant's workload
    const currentClaimCount = await this.claimRepository.countByClaimant(claimant.id);
    const maxClaims = claimant.maxConcurrentClaims ?? 5;
    if (currentClaimCount >= maxClaims) {
      throw new ClaimOperationError(
        'MAX_CLAIMS_EXCEEDED',
        `Cannot accept handoff: claimant ${claimant.name} at max capacity`
      );
    }

    // Update handoff record
    pendingHandoff.status = 'accepted';
    pendingHandoff.resolvedAt = new Date();

    // Transfer claim to new owner
    const previousClaimant = claim.claimant;
    claim.claimant = claimant;
    claim.status = 'active';
    claim.lastActivityAt = new Date();

    await this.claimRepository.save(claim);

    // Emit events

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Release or complete existing claims on the accepting agent before retrying the accept.
  2. Set claimant.maxConcurrentClaims explicitly to a value that reflects the agent's real capacity.
  3. Have the load balancer re-route the handoff target via getSwarmLoad to a less-loaded agent.
  4. Retry the accept after a short delay during transient load spikes.

Example fix

// before
const claimant = { id: 'agent-1', type: 'agent', name: 'worker-1' };
await claimService.acceptHandoff(issueId, claimant); // at capacity

// after
const claimant = { id: 'agent-1', type: 'agent', name: 'worker-1', maxConcurrentClaims: 10 };
const count = await claimRepo.countByClaimant(claimant.id);
if (count < (claimant.maxConcurrentClaims ?? 5)) {
  await claimService.acceptHandoff(issueId, claimant);
}
Defensive patterns

Strategy: validation

Validate before calling

const max = claimant.maxConcurrentClaims ?? 5;
const current = await claimRepository.countByClaimant(claimant.id);
if (current >= max) {
  // pick a different target or wait
  throw new Error(`${claimant.id} at capacity (${current}/${max})`);
}
await claimService.acceptHandoff(issueId, claimant);

Try / catch

try {
  await claimService.acceptHandoff(issueId, claimant);
} catch (e) {
  if (e instanceof ClaimOperationError && e.code === 'MAX_CLAIMS_EXCEEDED') {
    // re-route to a less-loaded agent or backoff and retry
    return rerouteHandoff(issueId);
  }
  throw e;
}

Prevention

When it happens

Trigger: An agent already at its concurrency limit accepts another handoff; maxConcurrentClaims is undefined so the default of 5 is enforced; a burst of handoffs routed to one agent pushes it over the limit between the load balancer's check and the accept call.

Common situations: Agent config omits maxConcurrentClaims and silently inherits 5; load balancer targeting is skewed toward one agent; long-running claims are not released promptly so the count never drops.

Related errors


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