{"record":{"id":"dab839af21281e5f","repo":"jdx/mise","slug":"incoming-application-requires-an-active-recovery-o","errorCode":null,"errorMessage":"incoming application requires an active recovery operation","messagePattern":"incoming application requires an active recovery operation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/scope.rs","lineNumber":234,"sourceCode":"                .collect();\n            writer.pending.checkpoint.labels = label.into_iter().map(str::to_owned).collect();\n            writer.operation_mut().message = label.map(str::to_owned);\n            if let Err(err) = writer.write_pending() {\n                warn!(\"history: could not persist the capture label: {err:#}\");\n            }\n        }\n    }\n\n    /// The protective checkpoint this operation took, if any.\n    pub(crate) fn before(&self) -> Option<(u64, String)> {\n        self.0\n            .as_ref()\n            .and_then(|shared| lock_unpoisoned(shared).before.clone())\n    }\n\n    pub(crate) fn validate_starting_head(&self, expected: Option<&str>) -> Result<()> {\n        let Some(shared) = &self.0 else {\n            eyre::bail!(\"incoming application requires an active recovery operation\");\n        };\n        let writer = lock_unpoisoned(shared);\n        if writer.starting_head.as_deref() != expected {\n            eyre::bail!(\n                \"local saved history changed since planning; nothing was applied; run pull again\"\n            );\n        }\n        Ok(())\n    }\n\n    /// Retakes the protective checkpoint, replacing the earlier one, when\n    /// files changed between it and the verified plan.\n    pub(crate) fn recapture_before(&self, paths: &[PathBuf]) -> Result<()> {\n        let Some(shared) = &self.0 else {\n            return Ok(());\n        };\n        let mut writer = lock_unpoisoned(shared);\n        // held across the reservation, the capture, and the removal: the","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/scope.rs#L216-L252","documentation":"validate_starting_head verifies that incoming history is applied against an active recovery operation writer. If the internal shared recovery state (self.0) is None, there is no active recovery operation to record the application against, so applying incoming changes is refused.","triggerScenarios":"Calling validate_starting_head (incoming history application) on a scope whose recovery writer has not been initialized — e.g. applying a pull plan without first starting/locking a recovery operation.","commonSituations":"Programmatic or partial use of the history apply path that skipped operation setup; a previous error dropped the recovery writer before apply; state dir missing the expected active operation.","solutions":["Start the recovery/apply operation through the normal API so the shared writer is initialized before applying incoming history","Re-run the full pull so planning and application happen in one operation","Check for earlier errors that aborted operation setup and fix the root cause"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if recovery_writer.is_none() {\n    return Err(\"start a recovery operation before applying incoming history\".into());\n}","typeGuard":null,"tryCatchPattern":"match apply_incoming() {\n    Err(e) if e.to_string().contains(\"active recovery operation\") => start_recovery_then_apply(),\n    other => other,\n}","preventionTips":["Always start the recovery operation through the normal API before applying","Do not swallow earlier setup errors that drop the writer","Re-run the full pull instead of invoking apply steps ad hoc"],"tags":["history","recovery","state-error"],"backgroundTag":"invalid-state-transition","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}