ruvnet/ruflo · error
Can only complete running tasks
Error message
Can only complete running tasks
What it means
Task.complete(output?) finalizes a task from the 'running' state only. This throw occurs when complete() is called on a task that is pending, queued, assigned, or already finished (completed/failed/cancelled) — including double completion and completion of cancelled work.
Source
Thrown at v3/@claude-flow/swarm/src/domain/entities/task.ts:206
}
/**
* 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();
}
/**
* Mark task as failed
*/
fail(error: string): void {
if (this._status !== 'running' && this._status !== 'assigned') {
throw new Error('Can only fail running or assigned tasks');
}
this._error = error;
this._retryCount++;
if (this._retryCount >= this._maxRetries) {
this._status = 'failed';View on GitHub (pinned to fa13ee4ad6)
Solutions
- Make completion idempotent: skip when task.status !== 'running' Guard against races by funnelling complete/fail through one state owner per task (version check or lock) If a cancel arrived first, drop the late completion (or surface it as a 'zombie result' metric) Never skip start() — the pipeline pending/queued -> assigned -> start -> complete must be followed in order
Example fix
// before await run(task); task.complete(result); // throws if cancelled/failed/double-completed meanwhile // after await run(task); if (task.status === 'running') task.complete(result); else reportZombieResult(task.id, result); // cancelled/failed/dup — log, don't throw
Defensive patterns
Strategy: type-guard
Validate before calling
if (task.status === 'running') task.complete(result); else reportZombieResult(task.id, result); // cancelled/failed/double-complete
Type guard
function isCompletable(t) { return t.status === 'running'; } Try / catch
try { task.complete(output); }
catch (e) {
if (e instanceof Error && e.message === 'Can only complete running tasks') return; // idempotent skip
throw e;
} Prevention
- Make completion idempotent at the call site (check status or dedupe by taskId) Single-flight complete/fail per task to break timeout-vs-success races
- Follow the full lifecycle; never complete an 'assigned' task that skipped start()
When it happens
Trigger: Calling complete() twice (second sees 'completed') Completing a task that was cancelled mid-run (status 'cancelled') Completing an 'assigned' task that never had start() called (fast-path shortcuts skipping start) Race: fail() already marked the task 'failed' (retries exhausted) when success arrived late Completion delivered to the wrong task instance after retry-based reassignment
Common situations: At-least-once completion events without idempotency Late success callbacks racing timeouts that already failed the task Workers cancelled (SIGTERM) while their completion message is in flight Optimistic-concurrency violations when two replicas complete the same task
Related errors
- Can only start assigned tasks
- Cannot cancel finished tasks
- Task ${taskId} not assigned to this agent
- Can only queue pending tasks
- Can only assign queued or pending tasks
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/24ade752330a713b.
Report an issue: GitHub.