{"record":{"id":"1e7b6996dae99225","repo":"jdx/mise","slug":"cannot-encrypt-non-file","errorCode":null,"errorMessage":"cannot encrypt non-file {}","messagePattern":"cannot encrypt non-file (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/shadow.rs","lineNumber":277,"sourceCode":"                    (\n                        \"120000\",\n                        path_bytes(&std::fs::read_link(&live)?).into_owned(),\n                    )\n                } else if metadata.is_file() {\n                    #[cfg(unix)]\n                    let executable = {\n                        use std::os::unix::fs::PermissionsExt;\n                        metadata.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                    (if executable { \"100755\" } else { \"100644\" }, bytes)\n                } else {\n                    bail!(\"cannot encrypt non-file {}\", display_path(&live));\n                };\n                let path = format!(\n                    \"{}/{}\",\n                    root.label,\n                    rel.to_str()\n                        .ok_or_else(|| eyre::eyre!(\"non-UTF-8 tracked path\"))?\n                        .replace('\\\\', \"/\")\n                );\n                let fingerprint = blake3::keyed_hash(&cache_key, &bytes).to_hex().to_string();\n                let cached = cache.get(&path).filter(|entry| {\n                    entry.fingerprint == fingerprint && entry.mode == mode && entry.scheme == scheme\n                });\n                let oid = match cached {\n                    Some(entry)\n                        if self\n                            .blob_starts_with(&entry.oid, b\"mise-encrypted-file-v1\\n\")\n                            .unwrap_or(false) =>\n                    {","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/shadow.rs#L259-L295","documentation":"During encrypted capture of tracked files, the code reads each live path and requires it to be a regular file (to read its bytes and record its blob mode). If the path is a directory, symlink-to-nothing, socket, or other non-file, it cannot be encrypted as a blob and capture bails naming the path.","triggerScenarios":"capture_tracked_files encounters a tracked `encrypt`-policy path that resolves at capture time to a non-regular file (e.g. a directory or dangling symlink).","commonSituations":"A tracked path that was a file becomes a directory after a refactor; a symlink target was deleted; a named pipe or socket now occupies a previously-regular path.","solutions":["Inspect the reported path and remove or correct it (or point tracking at a real file)","If it is a symlink to a missing target, restore the target or drop the symlink","If a directory now exists at the path, update the tracked paths/policy to target files within it"],"exampleFix":"// before: tracked path points at a directory\n\"secrets\": { \"path\": \"~/.config/secrets\", \"encrypt\": true }\n\n// after: track the actual files\n\"secrets\": { \"path\": \"~/.config/secrets/credentials.env\", \"encrypt\": true }","handlingStrategy":"validation","validationCode":"// Rust: verify each tracked encrypt-path is a regular file before capture\nfor path in tracked_paths {\n    let meta = std::fs::symlink_metadata(&path)?;\n    anyhow::ensure!(meta.is_file(), \"{} must be a regular file\", path.display());\n}","typeGuard":"fn is_regular_file(p: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(p).map(|m| m.is_file()).unwrap_or(false)\n}","tryCatchPattern":"match capture_tracked(&walk, recipients, interactive) {\n    Err(e) if e.to_string().contains(\"cannot encrypt non-file\") => {\n        eprintln!(\"tracked path is not a regular file; fix or untrack it: {e}\");\n    }\n    other => other?,\n}","preventionTips":["Avoid tracking symlinks and special files with encrypt policies","Re-check tracked paths after refactors that turn files into directories","Run a pre-capture linter that stats every tracked path"],"tags":["encryption","filesystem","capture"],"backgroundTag":"incompatible-source-type","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}