{"record":{"id":"36c81a6611060d5e","repo":"Hmbown/CodeWhale","slug":"fleet-run-is-already-terminal-lifecycle","errorCode":null,"errorMessage":"fleet run {} is already terminal ({lifecycle:?})","messagePattern":"fleet run (.+?) is already terminal \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/manager.rs","lineNumber":467,"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":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/manager.rs#L449-L485","documentation":"FleetManager::activate_run refuses to activate a run whose effective lifecycle is Completed, Failed, or Cancelled. The lifecycle comes from run_status_overrides if present, otherwise the derived run.status. Terminal runs are never reactivated because their ledger is closed; new work requires a new run or the explicit worker-restart control path.","triggerScenarios":"Calling FleetManager::activate_run(&run_id) on a run that finished (Completed/Failed) or was cancelled via stop_run/stop_all, including cases where a status override marks it terminal while the derived status does not.","commonSituations":"Retrying a failed fleet run by re-activating the same run id; a cancelled run whose id was reused from shell history; UI re-sending an activate command after the run completed in the background.","solutions":["Check run status first (status_from_state / run_status) and only call activate_run for non-terminal runs","Create a new fleet run with the same task specs instead of re-activating a terminal one","If the run was cancelled by mistake and must continue, use the documented worker restart path rather than activation","If you believe the terminal override is stale, reconcile the ledger state before retrying"],"exampleFix":"// before\nlet report = manager.activate_run(&run_id)?; // panics path: already terminal\n\n// after\nlet status = manager.run_status(&run_id)?;\nif !matches!(status.lifecycle, FleetRunStatus::Completed | FleetRunStatus::Failed | FleetRunStatus::Cancelled) {\n    let report = manager.activate_run(&run_id)?;\n} else {\n    // start a fresh run with the same specs\n}","handlingStrategy":"validation","validationCode":"fn run_is_terminal(status: &FleetRunStatus) -> bool {\n    matches!(\n        status,\n        FleetRunStatus::Completed | FleetRunStatus::Failed | FleetRunStatus::Cancelled\n    )\n}\n\n// before activating:\nlet st = manager.run_status(&run_id)?;\nassert!(!run_is_terminal(&st.lifecycle));","typeGuard":null,"tryCatchPattern":"match manager.activate_run(&run_id) {\n    Ok(report) => { /* activated */ }\n    Err(err) if err.to_string().contains(\"already terminal\") => {\n        // create a new run with the same specs instead\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Check lifecycle before every activate; treat terminal as permanent","Encode 'retry means new run' in automation scripts","Disable UI activate actions for terminal rows"],"tags":["fleet","lifecycle","invalid-state-transition","rust","codewhale"],"backgroundTag":"invalid-state-transition","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}