{"record":{"id":"5fe9eba075589d4e","repo":"Hmbown/CodeWhale","slug":"fleet-run-is-already-terminal-lifecycle-manager","errorCode":null,"errorMessage":"Fleet run {} is already terminal ({lifecycle:?})","messagePattern":"Fleet run (.+?) is already terminal \\((.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/manager.rs","lineNumber":484,"sourceCode":"    pub fn activate_run(&self, run_id: &FleetRunId) -> Result<FleetRunReport> {\n        let state = self.ledger.rebuild_state()?;\n        let run =\n            state\n                .runs\n                .get(&run_id.0)\n                .cloned()\n                .ok_or_else(|| FleetControlError::UnknownRun {\n                    run_id: run_id.0.clone(),\n                })?;\n        let lifecycle = state\n            .run_status_overrides\n            .get(&run_id.0)\n            .unwrap_or(&run.status);\n        if matches!(\n            lifecycle,\n            FleetRunStatus::Completed | FleetRunStatus::Failed | FleetRunStatus::Cancelled\n        ) {\n            bail!(\"Fleet run {} is already terminal ({lifecycle:?})\", run_id.0);\n        }\n        if !matches!(lifecycle, FleetRunStatus::Running) {\n            self.ledger\n                .update_run_status(run_id, FleetRunStatus::Running, &timestamp())?;\n        }\n        let state = self.ledger.rebuild_state()?;\n        let snapshot = self.status_from_state(Some(run_id), &state);\n        Ok(FleetRunReport {\n            run_id: run.id,\n            task_count: run.task_specs.len(),\n            leased: 0,\n            queued: snapshot.queued,\n            worker_ids: run\n                .worker_specs\n                .iter()\n                .map(|worker| worker.id.clone())\n                .collect(),\n            warnings: Vec::new(),","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/fleet/manager.rs#L466-L502","documentation":"FleetManager::activate_run transitions a durable queued run to Running for managed clients, but terminal runs are never reactivated through this method (crates/tui/src/fleet/manager.rs:484). If the run's effective lifecycle (status override or recorded status) is already Completed, Failed, or Cancelled, activation is refused — a terminal run is final, and worker restart is a separate explicit control.","triggerScenarios":"Calling activate_run (or start_run, which delegates to it) on a run whose ledger status or status override is Completed, Failed, or Cancelled — e.g. re-invoking start after the run finished, or after an operator cancelled it via a separate interrupt process.","commonSituations":"A managed client retries activation after a crash without checking run state; an operator re-runs a CLI start command for an already-finished run; a resumption script races with a cancellation.","solutions":["Check the run's status first (run_status / ledger state) and skip activation when it is already terminal.","Create a new run instead of reactivating the completed one — terminal is final by design.","If individual workers need rework, use the explicit worker restart control rather than run activation."],"exampleFix":"// before\nmanager.activate_run(&run_id)?;\n// after\nif !matches!(manager.run_status(&run_id)?.status, FleetRunStatus::Completed | FleetRunStatus::Failed | FleetRunStatus::Cancelled) {\n    manager.activate_run(&run_id)?;\n} else {\n    // start a new run or restart individual workers instead\n}","handlingStrategy":"validation","validationCode":"let status = manager.run_status(&run_id)?;\nif matches!(status.status, FleetRunStatus::Completed | FleetRunStatus::Failed | FleetRunStatus::Cancelled) {\n    // skip activation; run is final\n}","typeGuard":"fn is_terminal_run_status(s: &FleetRunStatus) -> bool { matches!(s, FleetRunStatus::Completed | FleetRunStatus::Failed | FleetRunStatus::Cancelled) }","tryCatchPattern":"if let Err(e) = manager.activate_run(&run_id) {\n    if e.to_string().contains(\"already terminal\") { /* treat as success/no-op or create a new run */ }\n    else { return Err(e.into()); }\n}","preventionTips":["Persist run state in managed clients and check it before re-issuing activate/start after a crash.","Treat terminal run state as final in all automation; restarts go through the worker-restart control.","Serialize start/cancel commands so a cancel cannot be followed by a racing activation."],"tags":["fleet","lifecycle","invalid-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}