{"record":{"id":"d50cb2a043bc36cc","repo":"gitbutlerapp/gitbutler","slug":"installation-path-is-a-file-not-a-directory-p","errorCode":null,"errorMessage":"Installation path {} is a file, not a directory. Please specify a directory path.","messagePattern":"Installation path (.+?) is a file, not a directory\\. Please specify a directory path\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but/src/command/skill/mod.rs","lineNumber":1115,"sourceCode":"    } else {\n        None\n    };\n    let installed_for_driving_agent = agent_default_path.is_some();\n    let install_paths = if let Some(custom) = custom_path {\n        vec![resolve_custom_path(&custom, ctx, global)?]\n    } else if detect {\n        detect_install_paths(ctx, global)?\n    } else if let Some(path) = agent_default_path {\n        vec![path]\n    } else {\n        vec![prompt_for_install_path(ctx, global, out, &mut progress)?]\n    };\n\n    let mut version = \"\";\n    for install_path in &install_paths {\n        // Validate installation path\n        if install_path.exists() && install_path.is_file() {\n            anyhow::bail!(\n                \"Installation path {} is a file, not a directory. Please specify a directory path.\",\n                install_path.display()\n            );\n        }\n\n        // Check if files already exist and warn user\n        if install_path.join(\"SKILL.md\").exists()\n            && let Some(writer) = out.for_human()\n        {\n            writeln!(writer)?;\n            writeln!(\n                writer,\n                \"{} Skill files already exist at {}\",\n                t.sym().warning,\n                t.config_value.paint(install_path.display().to_string())\n            )?;\n            writeln!(writer, \"  Overwriting existing files...\")?;\n            writeln!(writer)?;","sourceCodeStart":1097,"sourceCodeEnd":1133,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/command/skill/mod.rs#L1097-L1133","documentation":"Before writing skill files into a destination, `but skill install` validates that the path can hold a directory tree. If install_path exists and is a regular file (is_file()), creating SKILL.md and references/ under it is impossible, so the command fails with the offending path instead of attempting partial writes.","triggerScenarios":"Passing --path that resolves to an existing regular file - a leftover marker file named like the target directory, or a symlink pointing at a file rather than a directory.","commonSituations":"Dotfile layouts where ~/.claude/skills is a file, leftovers from earlier tooling, typos in --path colliding with an existing file name.","solutions":["Point --path at a directory path that is free or already a directory","Move or delete the conflicting file: `mv ~/.claude/skills ~/.claude/skills.bak`","If the path is a symlink, retarget it to a directory"],"exampleFix":"$ ls -l ~/.claude/skills    # regular file -> fails\n$ rm ~/.claude/skills\n$ but skill install --path ~/.claude/skills","handlingStrategy":"validation","validationCode":"match std::fs::metadata(&path) {\n    Ok(m) if m.is_file() => { /* pick another path or stop before invoking */ }\n    Ok(m) if m.is_dir() => { /* safe */ }\n    _ => { /* path free: also safe */ }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check fs::metadata on the target before installing","Follow symlinks when validating (metadata does; symlink_metadata does not)","In dotfiles repos, ensure skill directories are not shadowed by files"],"tags":["cli","filesystem","path","skill","install"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}