{"record":{"id":"b2de4288ae878313","repo":"jdx/mise","slug":"cannot-protect-reason","errorCode":null,"errorMessage":"cannot protect {}: {reason}","messagePattern":"cannot protect (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/checkpoint/preimages.rs","lineNumber":244,"sourceCode":"                    for link in links {\n                        self.snapshot(\n                            &path.join(&link.rel),\n                            &PathSnapshot::Symlink {\n                                dest: link.dest.clone(),\n                            },\n                            tree,\n                        )?;\n                    }\n                }\n            }\n            PathSnapshot::Directory { mode } => {\n                if self.eligible(path)? {\n                    self.mode(path, Some(*mode));\n                }\n            }\n            PathSnapshot::Unrecorded { reason, .. } => {\n                if self.eligible(path)? {\n                    eyre::bail!(\"cannot protect {}: {reason}\", display_path(path));\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::system::files::{FileMode, FilePolicy};\n    use crate::system::history::journal::Capture;\n\n    #[test]\n    fn first_preimages_survive_multiple_phases_without_saving_siblings() -> Result<()> {\n        let state = tempfile::tempdir()?;\n        let live =\n            tempfile::tempdir_in(crate::system::history::sync::layout::Roots::current().home)?;","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/checkpoint/preimages.rs#L226-L262","documentation":"When taking a history snapshot (`snapshot` in preimages.rs), each path resolves to a `PathSnapshot`. If a path is `Unrecorded` for some `reason` (e.g. unreadable, unsupported) but is still considered `eligible` for protection, mise cannot safely include it and bails with `cannot protect {path}: {reason}` instead of silently dropping a file it promised to protect.","triggerScenarios":"A path passes `eligible()` (so it's in the protection set) but its snapshot classification is `PathSnapshot::Unrecorded { reason, .. }` — e.g. unreadable file, permission issue, or unsupported file type discovered during snapshotting.","commonSituations":"A config/tracked file has restrictive permissions (mode 600 owned by another user), a file was deleted between listing and snapshotting, or a symlink/special file cannot be recorded.","solutions":["Fix the underlying reason shown in the message (e.g. `chmod`/`chown` the file so it's readable by the current user).","Exclude the problematic path from history tracking/config protection scope.","Restore the missing file if it disappeared mid-snapshot, then retry the snapshot."],"exampleFix":"# before: file unreadable\nls -l ~/.config/mise/secrets.toml  # -rw------- root root\n# after\nsudo chown $USER ~/.config/mise/secrets.toml && chmod u+rw ~/.config/mise/secrets.toml","handlingStrategy":"try-catch","validationCode":"if !path.exists() || std::fs::metadata(&path).map(|m| m.permissions().readonly()).unwrap_or(true) {\n    exclude_from_tracking(&path);\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().starts_with(\"cannot protect\") => fix_or_exclude(&path)?,\n    other => other?,\n}","preventionTips":["Ensure tracked files are readable by the user running mise","Exclude special/unreadable files from history tracking","Fix or restore files reported in the error message before retrying snapshots"],"tags":["history","snapshot","filesystem"],"backgroundTag":"file-read-failed","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"}