{"record":{"id":"72c0fb5302d21a63","repo":"jdx/mise","slug":"directory-contents-cannot-be-verified-safely-left","errorCode":null,"errorMessage":"directory contents cannot be verified safely; left untouched","messagePattern":"directory contents cannot be verified safely; left untouched","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/recovery.rs","lineNumber":100,"sourceCode":"    let Some(after) = after else {\n        bail!(\"write completion was not recorded; inspect the live file before retrying recovery\");\n    };\n    if PathState::observe(path) != *after {\n        bail!(\"changed after the operation; left untouched\");\n    }\n    // Entry count alone cannot establish a directory's identity. Never\n    // replace a populated directory on that evidence.\n    if matches!(after, PathState::Dir { entries, .. } if *entries != 0)\n        && !(matches!(prior, PathSnapshot::Directory { .. })\n            && matches!(\n                after,\n                PathState::Dir {\n                    identity: Some(_),\n                    ..\n                }\n            ))\n    {\n        bail!(\"directory contents cannot be verified safely; left untouched\");\n    }\n    validate_snapshot(state_dir, prior)?;\n    if PathState::observe(path) != *after {\n        bail!(\"changed while preparing recovery; left untouched\");\n    }\n    restore(state_dir, path, prior)\n}\n\nfn validate_destination(path: &Path) -> Result<()> {\n    if !path.is_absolute() || path.components().any(|c| matches!(c, Component::ParentDir)) {\n        bail!(\"invalid recovery destination\");\n    }\n    for parent in path.ancestors().skip(1) {\n        if std::fs::symlink_metadata(parent).is_ok_and(|meta| meta.is_symlink()) {\n            bail!(\"a parent directory is now a symlink; left untouched\");\n        }\n    }\n    Ok(())","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/recovery.rs#L82-L118","documentation":"When the recorded 'after' state is a non-empty directory (or the prior state was not itself a directory), entry counts alone cannot prove the directory's identity, so recovery refuses to act to avoid clobbering unknown contents. It bails before restore, also after re-checking that the path still matches the 'after' state.","triggerScenarios":"recover_path invoked where `after` is PathState::Dir with entries != 0 and it is not the case that both `prior` and the observed state were identity-verified directories — i.e. a populated directory whose contents cannot be validated against the snapshot.","commonSituations":"Recovering an interrupted operation that replaced a populated directory (e.g. a config folder) where the crash left no verifiable identity; user files added inside the directory since the operation; restoring directories whose entries were never fully snapshotted.","solutions":["Manually inspect the directory contents; remove or move files you do not need, then retry recovery.","Use `recover <operation> --keep-current` to explicitly accept the directory's current contents.","Recreate the directory structure via re-running bootstrap/enrollment instead of automatic restore.","Restore the directory from backup if its contents are wrong."],"exampleFix":"// before: populated dir blocks auto-restore\n$ mise bootstrap dotfiles recover\n// after: review the dir, then accept current contents\n$ ls ~/.config/tool/\n$ mise bootstrap dotfiles recover <operation> --keep-current","handlingStrategy":"validation","validationCode":"// before recovering, ensure the directory is reviewable\nlet populated = std::fs::read_dir(path).map(|d| d.count() > 0).unwrap_or(false);\nif populated { eprintln!(\"review contents; consider --keep-current\"); }","typeGuard":null,"tryCatchPattern":"match recover_entries(state_dir, plan) {\n    Err(e) if e.to_string().contains(\"cannot be verified safely\") => {\n        eprintln!(\"{e}\\ninspect the directory manually, then use --keep-current\");\n    }\n    other => other?,\n}","preventionTips":["Avoid interrupting operations that replace whole directories.","Don't add files into directories pending recovery.","Review populated directories manually before automatic restore."],"tags":["recovery","directory","safety","dotfiles"],"backgroundTag":"internal-invariant-violation","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"}