{"record":{"id":"5676ecdcd078ed15","repo":"ruvnet/ruflo","slug":"cannot-assign-task-to-terminated-agent","errorCode":null,"errorMessage":"Cannot assign task to terminated agent","messagePattern":"Cannot assign task to terminated agent","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/swarm/src/domain/entities/agent.ts","lineNumber":250,"sourceCode":"\n  /**\n   * Recover from error state\n   */\n  recover(): void {\n    if (this._status !== 'error') {\n      throw new Error('Can only recover from error state');\n    }\n    this._status = 'idle';\n    delete this._metadata['lastError'];\n    this._updatedAt = new Date();\n  }\n\n  /**\n   * Assign a task to this agent\n   */\n  assignTask(taskId: string): void {\n    if (this._status === 'terminated') {\n      throw new Error('Cannot assign task to terminated agent');\n    }\n    if (this._currentTaskIds.size >= this._maxConcurrentTasks) {\n      throw new Error('Agent at maximum concurrent task capacity');\n    }\n\n    this._currentTaskIds.add(taskId);\n    this._status = 'busy';\n    this._lastActiveAt = new Date();\n    this._updatedAt = new Date();\n  }\n\n  /**\n   * Complete a task\n   */\n  completeTask(taskId: string): void {\n    if (!this._currentTaskIds.has(taskId)) {\n      throw new Error(`Task ${taskId} not assigned to this agent`);\n    }","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/swarm/src/domain/entities/agent.ts#L232-L268","documentation":"Agent.assignTask(taskId) refuses new work when the agent's status is 'terminated'. Termination is terminal and cleared the agent's task set, so assigning work to it is a lifecycle violation rather than a capacity issue.","triggerScenarios":"A scheduler assigning from a stale agent list after the agent was terminated (e.g., during scale-down)\nassignTask() racing an operator-initiated terminate()\nRehydrated agent persisted as 'terminated' but still present in the coordinator's available-agents index Queue workers that never remove terminated agents from their pool","commonSituations":"Agent pool updated by membership events that arrive after task dispatch decisions were made Crash-recovery replays assigning old tasks to agents that terminate() had already retired Dynamic scaling where termination and task assignment come from different components","solutions":["Check agent.status !== 'terminated' before assignTask() (also covers the capacity case separately)\nRemove terminated agents from scheduling pools when terminate() is called\nRe-assign the task to another agent or create a replacement agent when the target is terminated\nSerialize terminate/dispatch per agent (e.g., per-agent mutex or command queue) to avoid the race"],"exampleFix":"// before\nconst agent = pool.pickAny();\nagent.assignTask(task.id); // throws if agent was terminated\n\n// after\nconst agent = pool.pick(a => a.status !== 'terminated' && a.status !== 'paused');\nif (!agent) throw new Error('no eligible agent');\nagent.assignTask(task.id);","handlingStrategy":"type-guard","validationCode":"if (agent.status === 'terminated') {\n  agent = pickEligibleAgent(pool); // or spawn one\n}\nagent.assignTask(taskId);","typeGuard":"const acceptsWork = (a) => a.status !== 'terminated' && a.status !== 'paused';\n// (also check capacity separately before assignTask)","tryCatchPattern":"try { agent.assignTask(taskId); }\ncatch (e) {\n  if (e instanceof Error && /terminated agent|maximum concurrent/.test(e.message)) {\n    return reassignElsewhere(taskId); // pick another agent or requeue\n  }\n  throw e;\n}","preventionTips":["Remove terminated agents from scheduling pools at terminate() time","Re-check agent status at dispatch time, not from a cached list","Serialize terminate() and assign() per agent to avoid the race"],"tags":["agent","task-assignment","state-machine","lifecycle","typescript"],"backgroundTag":"state-machine-invalid-transition","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}