ruvnet/ruflo · error

Can only assign queued or pending tasks

Error message

Can only assign queued or pending tasks

What it means

Task.assign(agentId) accepts only 'queued' or 'pending' statuses. This throw fires when assigning an agent to a task that is already assigned, running, or finished — the entity prevents two owners and post-start assignment.

Source

Thrown at v3/@claude-flow/swarm/src/domain/entities/task.ts:184

  // Business Logic
  // ============================================================================

  /**
   * Queue the task for execution
   */
  queue(): void {
    if (this._status !== 'pending') {
      throw new Error('Can only queue pending tasks');
    }
    this._status = 'queued';
  }

  /**
   * Assign task to an agent
   */
  assign(agentId: string): void {
    if (this._status !== 'queued' && this._status !== 'pending') {
      throw new Error('Can only assign queued or pending tasks');
    }
    this._assignedAgentId = agentId;
    this._status = 'assigned';
  }

  /**
   * Start task execution
   */
  start(): void {
    if (this._status !== 'assigned') {
      throw new Error('Can only start assigned tasks');
    }
    this._status = 'running';
    this._startedAt = new Date();
  }

  /**
   * Complete the task successfully

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Gate on task.status === 'queued' || task.status === 'pending' before assign() To reassign an already-assigned task, first task.cancel() (or let fail() reset it) so it returns to a legal pre-state Run a single dispatcher or use per-task locking to prevent concurrent assignment decisions Drop stale assignment decisions produced before a state change (compare statuses, last-write-wins with versioning)

Example fix

// before
function dispatch(task, agentId) { task.assign(agentId); } // throws on reassigned/running

// after
function dispatch(task, agentId) {
  if (task.status === 'queued' || task.status === 'pending') { task.assign(agentId); return true; }
  return false; // skip stale decisions
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (task.status === 'queued' || task.status === 'pending') {
  task.assign(agentId);
} else {
  handleStaleDecision(task); // already owned/started/finished
}

Type guard

function isAssignable(t) { return t.status === 'queued' || t.status === 'pending'; }

Try / catch

try { task.assign(agentId); }
catch (e) {
  if (e instanceof Error && e.message === 'Can only assign queued or pending tasks') return; // stale dispatch
  throw e;
}

Prevention

When it happens

Trigger: Assigning a task twice (status already 'assigned') Assigning a completed/failed/cancelled task (stale dispatcher decisions) Assigning a running task to a different agent to 'steal' it — the entity does not support work stealing Race between a scheduler assign and a worker that already started the task

Common situations: Multiple schedulers/dispatchers active over the same task list Failover logic that reassigns tasks without first failing/cancelling them Recovery scans assigning every task in the store regardless of status Duplicate dispatch messages (at-least-once delivery)

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/341e53bc1230c84e. Report an issue: GitHub.