{"record":{"id":"5961c0f22cc26737","repo":"jdx/mise","slug":"cannot-capture-non-file","errorCode":null,"errorMessage":"cannot capture non-file {}","messagePattern":"cannot capture non-file (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/system/history/shadow.rs","lineNumber":584,"sourceCode":"                    (\"160000\", crate::git::Git::new(&live).current_sha()?)\n                } else if meta.is_file() {\n                    #[cfg(unix)]\n                    let executable = {\n                        use std::os::unix::fs::PermissionsExt;\n                        meta.permissions().mode() & 0o100 != 0\n                    };\n                    #[cfg(not(unix))]\n                    let executable = false;\n                    let bytes = crate::agecrypt::read_bounded(\n                        std::fs::File::open(&live)?,\n                        crate::agecrypt::MAX_PLAINTEXT_BYTES,\n                    )?;\n                    (\n                        if executable { \"100755\" } else { \"100644\" },\n                        self.hash_blob(&bytes)?,\n                    )\n                } else {\n                    bail!(\"cannot capture non-file {}\", display_path(&live));\n                };\n                Ok(Overlay {\n                    path: rel\n                        .to_str()\n                        .ok_or_else(|| eyre::eyre!(\"non-UTF-8 tracked path\"))?\n                        .replace('\\\\', \"/\"),\n                    object: Some((mode.into(), oid)),\n                })\n            })();\n            match captured {\n                Ok(overlay) => overlays.push(overlay),\n                Err(error) => omitted.push(super::store::PathReason {\n                    path: display_path(&live),\n                    reason: format!(\"unreadable: {error:#}\"),\n                }),\n            }\n        }\n        self.compose(&self.empty_object(\"tree\")?, &overlays)","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/shadow.rs#L566-L602","documentation":"`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.","triggerScenarios":"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`).","commonSituations":"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.","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."],"exampleFix":"// before\nfiles = [\"~/**\"]\n// after\nfiles = [\"~/**\", \"!~/.run/**\", \"!~/**/*.sock\", \"!~/**/pipe*\"]","handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(&live)?;\nlet ok = meta.is_file() || meta.is_dir() || meta.file_type().is_symlink();\nif !ok { eprintln!(\"cannot capture: {}\", live.display()); }","typeGuard":"fn capturable_kind(meta: &std::fs::Metadata) -> Option<&'static str> {\n    if meta.file_type().is_symlink() { Some(\"symlink\") }\n    else if meta.is_dir() { Some(\"dir\") }\n    else if meta.is_file() { Some(\"file\") }\n    else { None }\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["filesystem","snapshot","git"],"backgroundTag":"incompatible-source-type","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}