{"record":{"id":"1f77386e6909e16e","repo":"jdx/mise","slug":"dotfiles-manifest-requires-the-source-to-b","errorCode":null,"errorMessage":"[dotfiles].\"{}\": manifest requires the source to be a directory: {}","messagePattern":"\\[dotfiles\\]\\.\"(.+?)\": manifest requires the source to be a directory: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/files.rs","lineNumber":416,"sourceCode":"}\n\n/// Validate the complete paths claimed by composed `[dotfiles]` entries.\n/// Directory copies and `symlink-each` entries may share directories, but no\n/// two entries may own the same leaf or require a directory where another\n/// entry places a leaf.\npub(crate) fn validate_composed_file_footprints(requests: &[FileRequest]) -> Result<()> {\n    let mut leaves: IndexMap<PathBuf, &FileRequest> = IndexMap::new();\n    let mut directories: IndexMap<PathBuf, &FileRequest> = IndexMap::new();\n    let mut symlink_each_identities: HashMap<(&Path, &Path), &FileRequest> = HashMap::new();\n\n    for request in requests {\n        // Tracking observes native files; it does not own an apply leaf.\n        // A tracked parent may contain independently managed destinations.\n        if request.mode == FileMode::Track {\n            continue;\n        }\n        if request.manifest.is_some() && request.source.exists() && !request.source.is_dir() {\n            bail!(\n                \"[dotfiles].\\\"{}\\\": manifest requires the source to be a directory: {}\",\n                request.target_raw,\n                request.source.display_user()\n            );\n        }\n        if request.mode == FileMode::SymlinkEach\n            && let Some(existing) = symlink_each_identities.insert(\n                (request.source.as_path(), request.target.as_path()),\n                request,\n            )\n        {\n            return Err(composed_file_footprint_conflict(\n                &request.target,\n                existing,\n                request,\n            ));\n        }\n        // A missing source has an unknown eventual shape, but it still claims","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/files.rs#L398-L434","documentation":"validate_composed_file_footprints enforces that a manifest-backed dotfile declaration owns a directory as its source, since the manifest enumerates the files inside it. A non-Track request that has a manifest but whose source path exists and is not a directory is rejected with this error; Track requests are skipped because they own no apply leaf.","triggerScenarios":"Calling any of the plan/validate entry points (plan_apply_with_active, the composed_symlink_each_* validators, tracking footprint checks) with a FileRequest where manifest.is_some(), source.exists(), and !source.is_dir() — i.e. a [dotfiles] entry points at a regular file while declaring a manifest.","commonSituations":"The user points a manifest dotfile at a single file instead of the directory containing it (e.g. source = \"~/dotfiles/gitconfig\" with a manifest, instead of source = \"~/dotfiles\"), or the source was replaced by a file after being a directory.","solutions":["Point the declaration's source at the directory that contains the files listed in the manifest.","Remove the manifest key if you intend to manage a single file directly.","Recreate the directory layout the manifest expects (move the file into the expected directory)."],"exampleFix":"# before: manifest with a file source\n[dotfiles.\"~/.config\"]\nsource = \"~/dotfiles/config.toml\"\nmanifest = \"manifest.toml\"\n\n# after: source is the directory\n[dotfiles.\"~/.config\"]\nsource = \"~/dotfiles\"\nmanifest = \"manifest.toml\"","handlingStrategy":"validation","validationCode":"# manifest sources must be directories\np=$(mise get dotfile-source ~/.config); [ -d \"$p\" ] || echo \"not a directory: $p\"","typeGuard":"const manifestSourceIsDir = (p: string) => fs.existsSync(p) && fs.statSync(p).isDirectory();","tryCatchPattern":null,"preventionTips":["Point manifest-backed entries at directories, not files","Omit the manifest key for single-file declarations","Re-check the source path after restructuring your dotfiles repo"],"tags":["rust","dotfiles","manifest","validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}