{"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":250,"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":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/scope.rs#L232-L268","documentation":"validate_starting_head confirms an incoming (pulled) history application is tied to an active recovery operation holding a shared writer. If no recovery operation is active (self.0 is None), applying incoming data is an invalid state, so it bails.","triggerScenarios":"Calling validate_starting_head on a scope whose shared recovery state was never initialized (no active recovery operation) — e.g. applying a pull's incoming changes without first acquiring/taking the recovery lock, or after the recovery state was dropped/finished.","commonSituations":"Scripting history pull/apply calls out of order; a crash or earlier error cleared the recovery state between plan and apply; running the apply step in a fresh process that never opened the recovery operation.","solutions":["Start the recovery/apply flow through the public API that takes the recovery lock first, then re-run pull/apply","Re-run pull to plan and apply in one properly locked session","Check that no earlier step aborted and cleared the recovery operation before apply"],"exampleFix":"// before: apply without active recovery\nscope.validate_starting_head(Some(head))?;\n// after: enter recovery first\nlet scope = HistoryScope::begin_recovery(&state_dir)?; // acquires lock\nscope.validate_starting_head(Some(head))?;","handlingStrategy":"validation","validationCode":"if !history.recovery_active() {\n    history.begin_recovery()?; // acquire recovery lock before validating\n}","typeGuard":null,"tryCatchPattern":"match scope.validate_starting_head(head) {\n    Err(e) if e.to_string().contains(\"active recovery operation\") => {\n        scope.begin_recovery()?;\n        scope.validate_starting_head(head)?;\n    }\n    other => other?,\n}","preventionTips":["Always run plan/apply within one locked recovery session","Don't drop or finish the recovery state between steps","Use the public pull/apply API instead of calling internals ad hoc"],"tags":["history","state","locking"],"backgroundTag":"invalid-state-transition","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}