jdx/mise · error

cannot encrypt non-file

Error message

cannot encrypt non-file {}

What it means

During encrypted-file capture, `capture_tracked_files` classifies each tracked encrypted path as a symlink, a regular file, or something else. Only symlinks and regular files can be read and encrypted; any other entry type (FIFO, socket, device node, or a path that vanished between listing and stat) hits this bail. It exists so the snapshot never attempts to read a non-regular file into an encrypted blob.

Solutions

  1. Exclude the non-file path from the encrypted-files policy (globs) so it is not tracked/encrypted.
  2. Verify what the path actually is (ls -l / stat); if it is a socket/FIFO/device, it cannot be snapshotted as a file.
  3. If the path is expected to be a regular file, re-run capture after the writer finishes — this can be a transient race with the file being replaced.
  4. Symlink to a regular file instead of tracking the non-file entry directly (symlinks are supported and captured as mode 120000).

Example fix

// before: policy tracks everything under a dir that contains a socket
files = ["~/.service/**"]
// after: exclude non-regular entries
files = ["~/.service/**", "!~/.service/daemon.sock"]
Defensive patterns

Strategy: validation

Validate before calling

let meta = std::fs::symlink_metadata(&live)?;
if !meta.is_file() && !meta.file_type().is_symlink() {
    eprintln!("skipping non-file: {}", live.display());
    return Ok(()); // or exclude from policy before capture
}

Type guard

fn is_capturable(meta: &std::fs::Metadata) -> bool {
    meta.is_file() || meta.file_type().is_symlink()
}

Prevention

When it happens

Trigger: A path matched by the encrypted-files policy is not a regular file or symlink when `capture_tracked` (via `capture_tracked_files`) stats it with `symlink_metadata`: e.g. a named pipe/socket/device node on the tracked path, or the entry was replaced/removed after the walk listed it (TOCTOU race, so `symlink_metadata` returned an other-type entry).

Common situations: Tracking an encrypted path that is actually a Unix socket or FIFO (e.g. a service socket under home), a device file, or a volatile path that swaps between file and non-file while a snapshot is taken. Also happens on filesystems where stat races occur under concurrent writes.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/1e7b6996dae99225. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/shadow.rs:426

                    (
                        "120000",
                        path_bytes(&std::fs::read_link(&live)?).into_owned(),
                    )
                } else if metadata.is_file() {
                    #[cfg(unix)]
                    let executable = {
                        use std::os::unix::fs::PermissionsExt;
                        metadata.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" }, bytes)
                } else {
                    bail!("cannot encrypt non-file {}", display_path(&live));
                };
                let path = format!(
                    "{}/{}",
                    root.label,
                    rel.to_str()
                        .ok_or_else(|| eyre::eyre!("non-UTF-8 tracked path"))?
                        .replace('\\', "/")
                );
                let fingerprint = blake3::keyed_hash(&cache_key, &bytes).to_hex().to_string();
                let cached = cache.get(&path).filter(|entry| {
                    entry.fingerprint == fingerprint && entry.mode == mode && entry.scheme == scheme
                });
                let oid = match cached {
                    Some(entry)
                        if self
                            .blob_starts_with(&entry.oid, b"mise-encrypted-file-v1\n")
                            .unwrap_or(false) =>
                    {

View on GitHub (pinned to 533346cc37)