{"record":{"id":"637a53ec82434892","repo":"gitbutlerapp/gitbutler","slug":"path-is-not-a-directory-path","errorCode":null,"errorMessage":"Path is not a directory: {path}","messagePattern":"Path is not a directory: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-api/src/open/mod.rs","lineNumber":539,"sourceCode":"        fn create_new_console(cmd: &mut Command) -> &mut Command {\n            use std::os::windows::process::CommandExt;\n            // CREATE_NEW_CONSOLE: Creates a new console for the process (0x00000010)\n            // This allows the terminal to run independently without blocking our thread\n            const CREATE_NEW_CONSOLE: u32 = 0x00000010;\n            cmd.creation_flags(CREATE_NEW_CONSOLE)\n        }\n        #[cfg(not(windows))]\n        fn create_new_console(cmd: &mut Command) -> &mut Command {\n            cmd\n        }\n\n        // Validate path exists and canonicalize it to proper Windows format\n        let path_buf = Path::new(&path);\n        if !path_buf.exists() {\n            bail!(\"Path does not exist: {path}\");\n        }\n        if !path_buf.is_dir() {\n            bail!(\"Path is not a directory: {path}\");\n        }\n\n        // Canonicalize to get the absolute, properly formatted Windows path\n        // This converts forward slashes to backslashes and resolves . and ..\n        let canonical_path = gix::path::realpath(path_buf)\n            .with_context(|| format!(\"Failed to canonicalize path: {path}\"))?\n            .to_str()\n            .context(\"BUG: input path is String, should be able to convert back to it\")?\n            .to_owned();\n        let canonical_path = &canonical_path;\n\n        // Check if the terminal binary exists in PATH before attempting to launch.\n        let binary_found = which::which(&terminal_id).is_ok();\n        if !binary_found {\n            return Err(anyhow::anyhow!(\"'{terminal_id}' was not found.\")\n                .context(but_error::Code::DefaultTerminalNotFound));\n        }\n","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-api/src/open/mod.rs#L521-L557","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait client.openInTerminal('wt', 'C:\\repo\\src\\main.rs'); // Path is not a directory\n\n// after\nimport { dirname } from 'path';\nawait client.openInTerminal('wt', dirname('C:\\repo\\src\\main.rs'));","handlingStrategy":"validation","validationCode":"import { stat } from 'fs/promises';\n\nconst info = await stat(targetPath).catch(() => null);\nif (!info) throw new Error(`Path does not exist: ${targetPath}`);\nif (!info.isDirectory()) {\n  targetPath = dirname(targetPath); // terminals need a directory\n}\nawait client.openInTerminal(terminalId, targetPath);","typeGuard":"async function isDirectory(p: string): Promise<boolean> {\n  try {\n    return (await stat(p)).isDirectory();\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  await client.openInTerminal(terminalId, path);\n} catch (e) {\n  if (String(e).startsWith('Path is not a directory')) {\n    await client.openInTerminal(terminalId, dirname(path));\n  } else throw e;\n}","preventionTips":["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"],"tags":["terminal","windows","filesystem","path","validation","rust"],"backgroundTag":"path-not-a-directory","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}