{"record":{"id":"d47bbfc117641c75","repo":"ruvnet/ruflo","slug":"can-only-start-assigned-tasks","errorCode":null,"errorMessage":"Can only start assigned tasks","messagePattern":"Can only start assigned tasks","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/swarm/src/domain/entities/task.ts","lineNumber":195,"sourceCode":"  }\n\n  /**\n   * Assign task to an agent\n   */\n  assign(agentId: string): void {\n    if (this._status !== 'queued' && this._status !== 'pending') {\n      throw new Error('Can only assign queued or pending tasks');\n    }\n    this._assignedAgentId = agentId;\n    this._status = 'assigned';\n  }\n\n  /**\n   * Start task execution\n   */\n  start(): void {\n    if (this._status !== 'assigned') {\n      throw new Error('Can only start assigned tasks');\n    }\n    this._status = 'running';\n    this._startedAt = new Date();\n  }\n\n  /**\n   * Complete the task successfully\n   */\n  complete(output?: unknown): void {\n    if (this._status !== 'running') {\n      throw new Error('Can only complete running tasks');\n    }\n    this._status = 'completed';\n    this._output = output;\n    this._completedAt = new Date();\n  }\n\n  /**","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/swarm/src/domain/entities/task.ts#L177-L213","documentation":"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.","triggerScenarios":"Worker pulling a 'pending' task and starting it directly without assign()\nDouble start(): two workers (or a retry) invoking start() on the same task — the second sees 'running'\nStarting a task after cancel() or after complete() (stale work queue entries)\nScheduler calling start() when it meant assign()","commonSituations":"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","solutions":["Always assign(agentId) first, then start(); never start an unassigned task","Check task.status === 'assigned' before start(); treat 'running' as already-in-progress (idempotent skip)\nUse task claims/leases so only one worker owns a task at a time\nDiscard stale task references after status changes; re-query the task before acting"],"exampleFix":"// before\nworker.on('task', t => { t.start(); doWork(t); }); // 'pending' task -> throws\n\n// after\nworker.on('task', t => {\n  if (t.status === 'pending' || t.status === 'queued') t.assign(worker.id);\n  if (t.status === 'assigned') t.start(); // only first worker passes\n  else return; // already running elsewhere\n  doWork(t);\n});","handlingStrategy":"type-guard","validationCode":"if (task.status === 'pending' || task.status === 'queued') task.assign(workerId); // claim first\nif (task.status === 'assigned') { task.start(); run(task); }\nelse skip(); // someone else started it","typeGuard":"function isStartable(t) { return t.status === 'assigned'; }","tryCatchPattern":"try { task.start(); }\ncatch (e) {\n  if (e instanceof Error && e.message === 'Can only start assigned tasks') return; // claimed elsewhere or stale\n  throw e;\n}","preventionTips":["Use task claims/leases so exactly one worker owns a task","Never skip assign(); there is no direct pending -> running shortcut","On worker restart, re-query task status instead of resuming from memory"],"tags":["task","state-machine","task-lifecycle","typescript"],"backgroundTag":"state-machine-invalid-transition","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}