jdx/mise · error
cannot protect
Error message
cannot protect {}: {reason} What it means
During a preimage snapshot, snapshot walks paths and records their modes when they are eligible for protection. If a path resolves to PathSnapshot::Unrecorded with a reason (e.g. the path's state could not be captured), and the path is otherwise eligible, protecting history would be incomplete, so it aborts with 'cannot protect <path>: <reason>'. The bundled reason explains exactly why the snapshot could not record it.
Solutions
- Read the <reason> in the message and fix the underlying cause (permissions, file type, or file churn) for the named path
- Exclude the problematic path from history protection via the history ignore/exclusion configuration
- Re-run the operation after the path is stable; transient races (file replaced during snapshot) usually resolve on retry
Example fix
// before: snapshot fails on an unreadable path // cannot protect /etc/secret.key: permission denied // after: exclude the path from protection // [history] // ignore = ["/etc/secret.key"]
Defensive patterns
Strategy: try-catch
Validate before calling
# ensure paths are snapshot-able before enabling protection for f in $WATCHED_PATHS; do [ -r "$f" ] || echo "warning: $f not readable; exclude it from history protection" >&2 done
Try / catch
match history::snapshot() {
Err(e) if e.to_string().starts_with("cannot protect") => {
// parse the path and reason, exclude the path, retry once
}
r => r?,
} Prevention
- Exclude special files (sockets, fifos) and unreadable secrets from protection
- Avoid mutating watched paths while a snapshot runs
- Read the reason in the message; it names the exact filesystem problem
When it happens
Trigger: Calling the history snapshot/preimage capture for a path that is eligible for protection but whose PathSnapshot came back Unrecorded { reason }, e.g. unreadable files, files that changed mid-snapshot, or unsupported file types.
Common situations: History protection enabled while the working tree contains files the snapshotter cannot record: permission-restricted files, sockets/fifos, rapidly changing files, or paths deleted between listing and snapshot.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- cannot verify current files for restoration
- cannot be recreated as a directory: something else is there…
- cannot capture non-file
- cannot encrypt non-file
- cannot safely change
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/b2de4288ae878313.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/checkpoint/preimages.rs:244
for link in links {
self.snapshot(
&path.join(&link.rel),
&PathSnapshot::Symlink {
dest: link.dest.clone(),
},
tree,
)?;
}
}
}
PathSnapshot::Directory { mode } => {
if self.eligible(path)? {
self.mode(path, Some(*mode));
}
}
PathSnapshot::Unrecorded { reason, .. } => {
if self.eligible(path)? {
eyre::bail!("cannot protect {}: {reason}", display_path(path));
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::system::files::{FileMode, FilePolicy};
use crate::system::history::journal::Capture;
#[test]
fn first_preimages_survive_multiple_phases_without_saving_siblings() -> Result<()> {
let state = tempfile::tempdir()?;
let live =
tempfile::tempdir_in(crate::system::history::sync::layout::Roots::current().home)?;View on GitHub (pinned to 533346cc37)