jdx/mise · error
cannot capture non-file {}
Error message
cannot capture non-file {} What it means
The non-encrypted capture path (tree_for_root via capture) hashes each live tracked path as a git blob, which requires a regular file. If a tracked path is not a regular file at capture time (directory, dangling symlink, special file), the snapshot cannot represent it and capture bails naming the path.
Source
Thrown at src/system/history/shadow.rs:435
("160000", crate::git::Git::new(&live).current_sha()?)
} else if meta.is_file() {
#[cfg(unix)]
let executable = {
use std::os::unix::fs::PermissionsExt;
meta.permissions().mode() & 0o100 != 0
};
#[cfg(not(unix))]
let executable = false;
let bytes = crate::agecrypt::read_bounded(
std::fs::File::open(&live)?,
crate::agecrypt::MAX_PLAINTEXT_BYTES,
)?;
(
if executable { "100755" } else { "100644" },
self.hash_blob(&bytes)?,
)
} else {
bail!("cannot capture non-file {}", display_path(&live));
};
Ok(Overlay {
path: rel
.to_str()
.ok_or_else(|| eyre::eyre!("non-UTF-8 tracked path"))?
.replace('\\', "/"),
object: Some((mode.into(), oid)),
})
})();
match captured {
Ok(overlay) => overlays.push(overlay),
Err(error) => omitted.push(super::store::PathReason {
path: display_path(&live),
reason: format!("unreadable: {error:#}"),
}),
}
}
self.compose(&self.empty_object("tree")?, &overlays)View on GitHub (pinned to afd2eddd3a)
Solutions
- Check the reported path with `ls -la` and remove or fix the non-file entry
- Restore a deleted symlink target or delete the dangling symlink
- Update the tracked-path configuration to reference actual files, then re-run capture
Example fix
// before: dangling symlink at tracked path ~/.config/app/settings.json -> /mnt/usb/settings.json (missing) // after: real file present ~/.config/app/settings.json (regular file)
Defensive patterns
Strategy: validation
Validate before calling
// Rust: stat all tracked paths before calling capture
for p in &tracked {
let m = std::fs::symlink_metadata(p)
.with_context(|| format!("tracked path missing: {}", p.display()))?;
anyhow::ensure!(m.is_file(), "tracked path is not a file: {}", p.display());
} Type guard
fn tracked_is_file(p: &std::path::Path) -> bool {
std::fs::symlink_metadata(p).map(|m| m.is_file()).unwrap_or(false)
} Try / catch
if let Err(e) = capture(&roots) {
if e.to_string().starts_with("cannot capture non-file") {
eprintln!("snapshot skipped: {e}; fix the path and re-run");
} else { return Err(e); }
} Prevention
- Keep tracked paths pointed at regular files, not directories or FIFOs
- Restore or remove dangling symlinks promptly
- Expect races: re-run capture if a file was deleted mid-walk
When it happens
Trigger: capture (via tree_for_root) walks a tracked root and a tracked path resolves to a non-regular file when it attempts to read and hash its bytes.
Common situations: A path previously tracked as a file was replaced by a directory; a symlink's target was removed; a FIFO/socket appeared at a tracked path; filesystem race where the file was deleted between the walk and the read.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- {other:?}
- cannot protect {}: {reason}
- cannot encrypt non-file {}
- rustc output is not a regular file: {}
- failed to create file symlink: {err}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/5961c0f22cc26737.
Report an issue: GitHub.