jdx/mise · warning
cannot capture non-file
Error message
cannot capture non-file {} What it means
`tree_for_root`, called during snapshot capture, hashes each tracked file into the scratch git tree. Symlinks, directories (gitlinks), and regular files are handled; any other entry type reported by `symlink_metadata` reaches this bail. Unlike the capture of regular tracked files (which records omissions as warnings), this error path aborts building the overlay for that file and it is recorded as omitted with this reason.
Solutions
- Refine tracked globs to exclude sockets/FIFOs/device nodes on those paths.
- Check the entry with `stat`; only regular files, directories, and symlinks can be captured.
- Re-run capture if the path was mid-replacement — a transient race can yield a non-file type.
- Point tracking at the regular file itself rather than a directory wildcard that sweeps in special files.
Example fix
// before files = ["~/**"] // after files = ["~/**", "!~/.run/**", "!~/**/*.sock", "!~/**/pipe*"]
Defensive patterns
Strategy: validation
Validate before calling
let meta = std::fs::symlink_metadata(&live)?;
let ok = meta.is_file() || meta.is_dir() || meta.file_type().is_symlink();
if !ok { eprintln!("cannot capture: {}", live.display()); } Type guard
fn capturable_kind(meta: &std::fs::Metadata) -> Option<&'static str> {
if meta.file_type().is_symlink() { Some("symlink") }
else if meta.is_dir() { Some("dir") }
else if meta.is_file() { Some("file") }
else { None }
} Prevention
- Exclude runtime directories (.run, sockets, pipes) from tracked globs.
- Note: unlike regular captures, tree_for_root records this as an omission — check capture warnings.
- Re-capture if a path was mid-replacement during snapshotting.
When it happens
Trigger: A tracked path resolves to a non-file, non-directory, non-symlink entry (FIFO, socket, device node) or races to one between the walk and the stat in `tree_for_root` (called from `capture`).
Common situations: A tracked glob accidentally matches a socket or pipe (e.g. ~/.cache/** matching runtime sockets), a dev machine with odd device nodes under home, or a path being atomically replaced via a special file during capture.
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
- cannot encrypt non-file
- cannot protect
- cannot safely change
- cannot verify current files for restoration
- could not snapshot the local history repository for preview
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/5961c0f22cc26737.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/shadow.rs:584
("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 533346cc37)