gitbutlerapp/gitbutler · error

Installation path is a file, not a directory. Please…

Error message

Installation path {} is a file, not a directory. Please specify a directory path.

What it means

Before installing, `install_skill` validates each resolved installation path. If a path exists on disk but is a regular file rather than a directory, writing skill files into it is impossible, so it bails with a message naming the offending path.

Solutions

  1. Remove or rename the file at the target path and re-run.
  2. Pass `--path <dir>` pointing to a directory (create it first with mkdir -p if needed).
  3. Check the resolved path (it's printed in the error) and correct it.

Example fix

// before
but skill install --path ~/.claude/skills/gitbutler.md
// error: Installation path ... is a file, not a directory.

// after
mkdir -p ~/.claude/skills/gitbutler && but skill install --path ~/.claude/skills/gitbutler
Defensive patterns

Strategy: validation

Validate before calling

# shell
[ -f "$SKILL_PATH" ] && { echo "$SKILL_PATH is a file"; exit 1; }
mkdir -p "$SKILL_PATH"

Try / catch

if [ -f "$SKILL_PATH" ]; then rm -i "$SKILL_PATH"; fi
but skill install --path "$SKILL_PATH"

Prevention

When it happens

Trigger: Running `but skill install --path <file>` (or `--detect` resolving to a path) where the target exists and `is_file()` is true, e.g. pointing at `~/.claude/skills/gitbutler.md` or a symlink to a file.

Common situations: Typos in the path pointing at a file; previously created a file at that location; agent configs where the skills entry is a file, not a directory.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/d50cb2a043bc36cc. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/command/skill/mod.rs:1305

    } else {
        None
    };
    let installed_for_driving_agent = agent_default_path.is_some();
    let install_paths = if let Some(custom) = custom_path {
        vec![resolve_custom_path(&custom, ctx, global)?]
    } else if detect {
        detect_install_paths(ctx, global)?
    } else if let Some(path) = agent_default_path {
        vec![path]
    } else {
        vec![prompt_for_install_path(ctx, global, out, &mut progress)?]
    };

    let mut version = "";
    for install_path in &install_paths {
        // Validate installation path
        if install_path.exists() && install_path.is_file() {
            anyhow::bail!(
                "Installation path {} is a file, not a directory. Please specify a directory path.",
                install_path.display()
            );
        }

        // Check if files already exist and warn user
        if install_path.join("SKILL.md").exists()
            && let Some(writer) = out.for_human()
        {
            writeln!(writer)?;
            writeln!(
                writer,
                "{} Skill files already exist at {}",
                t.sym().warning,
                t.config_value.paint(install_path.display().to_string())
            )?;
            writeln!(writer, "  Overwriting existing files...")?;
            writeln!(writer)?;

View on GitHub (pinned to 58e5313667)