{"record":{"id":"0358a2c4098adbd4","repo":"jdx/mise","slug":"cannot-safely-change-without-a-recovery-preimag","errorCode":null,"errorMessage":"cannot safely change {} without a recovery preimage: {reason}","messagePattern":"cannot safely change (.+?) without a recovery preimage: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/journal.rs","lineNumber":534,"sourceCode":"}\n\n/// [`begin_changes`] with a capture depth per path. Fails when an entry\n/// cannot be persisted: the mutation must not go ahead without its\n/// write-ahead record.\npub(crate) fn begin_changes_with(\n    part: &str,\n    item: &str,\n    paths: impl IntoIterator<Item = (PathBuf, Capture)>,\n) -> Result<Vec<PendingChange>> {\n    if !super::scope::is_active() {\n        return Ok(vec![]);\n    }\n    let mut pending = vec![];\n    for (path, capture) in paths {\n        let prior = PathSnapshot::capture_with(&dirs::STATE, &path, capture);\n        if let PathSnapshot::Unrecorded { reason, .. } = &prior {\n            if super::scope::requires_recovery_preimage() {\n                eyre::bail!(\n                    \"cannot safely change {} without a recovery preimage: {reason}\",\n                    display_path(&path)\n                );\n            }\n            warn!(\n                \"bootstrap: temporary recovery is unavailable for {}: {reason}; proceeding with deployment\",\n                display_path(&path)\n            );\n        }\n        if let Some(seq) = super::scope::record(JournalEntry::PathChanged {\n            part: part.to_string(),\n            item: item.to_string(),\n            path: path.clone(),\n            prior,\n        })? {\n            pending.push(PendingChange { seq, path });\n        }\n    }","sourceCodeStart":516,"sourceCodeEnd":552,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/journal.rs#L516-L552","documentation":"`begin_changes_with` captures pre-change snapshots of configured paths so changes can be reverted. In a scope that `requires_recovery_preimage()` (a strict/recovery mode), a path whose prior state is `PathSnapshot::Unrecorded` (e.g. the file could not be captured, does not exist in STATE, or the capture backend was unavailable) makes a safe change impossible, so it aborts instead of proceeding without a rollback image.","triggerScenarios":"Calling `begin_changes_with` (via `begin_changes`) while in a recovery-requiring scope and a target path's snapshot returns `Unrecorded { reason, .. }` — e.g. the path does not exist yet, permission to read it failed, or no prior recorded state exists in `dirs::STATE`.","commonSituations":"Running a deployment/recovery operation against files that were never bootstrapped into history state; files deleted or moved between planning and capture; read-permission problems under a different user or sandbox.","solutions":["Run the non-strict bootstrap path first so the path gets a recorded preimage (the same code falls back to a warning when recovery is not required)","Create/restore the target file at the expected location before beginning changes","Fix read permissions on the path for the user running the tool","Inspect the `{reason}` in the message to identify which capture condition failed and address it directly"],"exampleFix":"// before\nbegin_changes_with(paths)?; // fails in recovery scope\n// after\nif !path.exists() { fs::create(path)?; }\nbegin_changes_with(paths)?;","handlingStrategy":"validation","validationCode":"// verify each path is capturable before beginning changes\nfor path in paths {\n  let snap = PathSnapshot::capture_with(&dirs::STATE, &path, capture);\n  if let PathSnapshot::Unrecorded { reason, .. } = snap {\n    if requires_recovery_preimage() {\n      return Err(format!(\"{path}: {reason}\"));\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"match begin_changes_with(paths) {\n  Err(e) if e.to_string().starts_with(\"cannot safely change\") => {\n    // bootstrap/repair the path, then retry once\n  }\n  other => other,\n}","preventionTips":["Run the non-strict bootstrap at least once so all managed paths have recorded preimages","Check path existence and readability before beginning changes in recovery scopes","Keep files managed by this tool from being deleted/moved externally between planning and capture"],"tags":["state","recovery","filesystem","snapshot"],"backgroundTag":"missing-required-config","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"}