gitbutlerapp/gitbutler · error · anyhow::Error
Path is not a directory: {path}
Error message
Path is not a directory: {path} What it means
Windows arm of open_in_terminal (crates/but-api/src/open/mod.rs:538-540): the path exists but is not a directory. Terminals are launched with the path as working directory (or via `wt -d`), so a file path is rejected before canonicalization.
Source
Thrown at crates/but-api/src/open/mod.rs:539
fn create_new_console(cmd: &mut Command) -> &mut Command {
use std::os::windows::process::CommandExt;
// CREATE_NEW_CONSOLE: Creates a new console for the process (0x00000010)
// This allows the terminal to run independently without blocking our thread
const CREATE_NEW_CONSOLE: u32 = 0x00000010;
cmd.creation_flags(CREATE_NEW_CONSOLE)
}
#[cfg(not(windows))]
fn create_new_console(cmd: &mut Command) -> &mut Command {
cmd
}
// Validate path exists and canonicalize it to proper Windows format
let path_buf = Path::new(&path);
if !path_buf.exists() {
bail!("Path does not exist: {path}");
}
if !path_buf.is_dir() {
bail!("Path is not a directory: {path}");
}
// Canonicalize to get the absolute, properly formatted Windows path
// This converts forward slashes to backslashes and resolves . and ..
let canonical_path = gix::path::realpath(path_buf)
.with_context(|| format!("Failed to canonicalize path: {path}"))?
.to_str()
.context("BUG: input path is String, should be able to convert back to it")?
.to_owned();
let canonical_path = &canonical_path;
// Check if the terminal binary exists in PATH before attempting to launch.
let binary_found = which::which(&terminal_id).is_ok();
if !binary_found {
return Err(anyhow::anyhow!("'{terminal_id}' was not found.")
.context(but_error::Code::DefaultTerminalNotFound));
}
View on GitHub (pinned to caf1f223d3)
Solutions
- Pass the repository's directory (the project worktree root), not a file within it
- Derive the directory with path.dirname() when starting from a file path
- For revealing files rather than opening a terminal, use the reveal-in-file-manager action instead
Example fix
// before
await client.openInTerminal('wt', 'C:\repo\src\main.rs'); // Path is not a directory
// after
import { dirname } from 'path';
await client.openInTerminal('wt', dirname('C:\repo\src\main.rs')); Defensive patterns
Strategy: validation
Validate before calling
import { stat } from 'fs/promises';
const info = await stat(targetPath).catch(() => null);
if (!info) throw new Error(`Path does not exist: ${targetPath}`);
if (!info.isDirectory()) {
targetPath = dirname(targetPath); // terminals need a directory
}
await client.openInTerminal(terminalId, targetPath); Type guard
async function isDirectory(p: string): Promise<boolean> {
try {
return (await stat(p)).isDirectory();
} catch {
return false;
}
} Try / catch
try {
await client.openInTerminal(terminalId, path);
} catch (e) {
if (String(e).startsWith('Path is not a directory')) {
await client.openInTerminal(terminalId, dirname(path));
} else throw e;
} Prevention
- Always pass a directory (repo root) to openInTerminal, never a file
- Derive the directory with dirname() when starting from a selected file
- Use the file-reveal action for showing files, not the terminal action
When it happens
Trigger: Passing a file inside the repo (e.g. a source file or .sln) instead of the repository directory; a path that resolves to a file because a trailing segment was truncated or appended.
Common situations: Frontend handing the selected file's path to the terminal action; scripts deriving the path from a file URI without dirname; junction/symlink pointing at a file.
Related errors
- Path does not exist: {path}
- Unknown terminal: {terminal_id}
- The path {} does not exist
- The path {} is not a directory
- When using OpenAI in a bring your own key configuration, you
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/637a53ec82434892.
Report an issue: GitHub.