ruvnet/ruflo · error

Can only start assigned tasks

Error message

Can only start assigned tasks

What it means

Task.start() moves a task from 'assigned' to 'running' and refuses anything else. This throw means start() was called on a task that was never assigned, was already started, or already finished — the entity requires the strict pending/queued -> assigned -> running order.

Source

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

  }

  /**
   * 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
   */
  complete(output?: unknown): void {
    if (this._status !== 'running') {
      throw new Error('Can only complete running tasks');
    }
    this._status = 'completed';
    this._output = output;
    this._completedAt = new Date();
  }

  /**

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Always assign(agentId) first, then start(); never start an unassigned task
  2. Check task.status === 'assigned' before start(); treat 'running' as already-in-progress (idempotent skip) Use task claims/leases so only one worker owns a task at a time Discard stale task references after status changes; re-query the task before acting

Example fix

// before
worker.on('task', t => { t.start(); doWork(t); }); // 'pending' task -> throws

// after
worker.on('task', t => {
  if (t.status === 'pending' || t.status === 'queued') t.assign(worker.id);
  if (t.status === 'assigned') t.start(); // only first worker passes
  else return; // already running elsewhere
  doWork(t);
});
Defensive patterns

Strategy: type-guard

Validate before calling

if (task.status === 'pending' || task.status === 'queued') task.assign(workerId); // claim first
if (task.status === 'assigned') { task.start(); run(task); }
else skip(); // someone else started it

Type guard

function isStartable(t) { return t.status === 'assigned'; }

Try / catch

try { task.start(); }
catch (e) {
  if (e instanceof Error && e.message === 'Can only start assigned tasks') return; // claimed elsewhere or stale
  throw e;
}

Prevention

When it happens

Trigger: Worker pulling a 'pending' task and starting it directly without assign() Double start(): two workers (or a retry) invoking start() on the same task — the second sees 'running' Starting a task after cancel() or after complete() (stale work queue entries) Scheduler calling start() when it meant assign()

Common situations: Queue consumers starting tasks they fetched by query rather than via the assignment flow Crash recovery where a worker restarts and re-starts tasks it already began Duplicate delivery of the same task to two workers

Related errors


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