{"record":{"id":"035a778e7ac88ce5","repo":"gitbutlerapp/gitbutler","slug":"project-path-is-not-a-directory","errorCode":null,"errorMessage":"project path is not a directory","messagePattern":"project path is not a directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-project/src/project.rs","lineNumber":321,"sourceCode":"    /// This is for tests only.\n    ///\n    /// Unwraps the `Project` if the project was actually added.\n    /// Panics if it was not.\n    pub fn unwrap_project(self) -> Project {\n        match self {\n            AddProjectOutcome::Added(p) => p,\n            _ => panic!(\"called `AddProjectOutcome::unwrap_project()` on a non-project outcome\"),\n        }\n    }\n\n    /// Try to get the `Project`, returning an error if it was not added.\n    pub fn try_project(self) -> anyhow::Result<Project> {\n        match self {\n            AddProjectOutcome::Added(p) => Ok(p),\n            AddProjectOutcome::AlreadyExists(_) => Err(anyhow::anyhow!(\"project already exists\")),\n            AddProjectOutcome::PathNotFound => Err(anyhow::anyhow!(\"project path not found\")),\n            AddProjectOutcome::NotADirectory => {\n                Err(anyhow::anyhow!(\"project path is not a directory\"))\n            }\n            AddProjectOutcome::BareRepository => {\n                Err(anyhow::anyhow!(\"bare repositories are not supported\"))\n            }\n            AddProjectOutcome::NonMainWorktree => {\n                Err(anyhow::anyhow!(\"non-main worktrees are not supported\"))\n            }\n            AddProjectOutcome::NoWorkdir => Err(anyhow::anyhow!(\"no workdir found for repository\")),\n            AddProjectOutcome::NoDotGitDirectory => {\n                Err(anyhow::anyhow!(\"no .git directory found in repository\"))\n            }\n            AddProjectOutcome::ReftableRefFormatUnsupported => Err(anyhow::anyhow!(\n                \"unsupported repository reference format: reftable\"\n            )),\n            AddProjectOutcome::NotAGitRepository(msg) => {\n                Err(anyhow::anyhow!(\"not a git repository: {msg}\"))\n            }\n        }","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-project/src/project.rs#L303-L339","documentation":"AddProjectOutcome::NotADirectory mapped through try_project: the given path exists but is a file (or special node), not a directory. add_project requires a directory because a repository worktree is a directory tree.","triggerScenarios":"add_project pointed at a file (e.g. the .git file of a worktree, a symlink to a file, or the repo URL pasted instead of a path); a symlink whose target is a regular file; selecting the repo's .git file in a picker.","commonSituations":"Users pasting a URL or archive file instead of the cloned folder; drag-and-drop dropping a file; paths like repo/.git or repo/.git/config passed by tooling that assumed file-based selection.","solutions":["Check path.is_dir() before calling add_project","If a symlink, resolve it (fs::canonicalize) and validate the target is a directory","Point at the worktree root (the folder containing .git), never at a file inside it","If the user supplied a URL, clone it first, then add the clone"],"exampleFix":"// before\nlet project = add_project(&path, ...).try_project()?; // file path -> 'not a directory'\n\n// after\nif !path.is_dir() {\n    anyhow::bail!(\"{} is not a directory — pass the repository worktree root\", path.display());\n}\nlet project = add_project(&path, ...).try_project()?;","handlingStrategy":"validation","validationCode":"let path = std::fs::canonicalize(&input)?;\nif !path.is_dir() {\n    anyhow::bail!(\"{} is a file — pass the repository worktree root\", path.display());\n}","typeGuard":"fn is_directory(p: impl AsRef<Path>) -> bool {\n    p.as_ref().is_dir() // follows symlinks; false for files\n}","tryCatchPattern":"match add_project(&path, ...) {\n    AddProjectOutcome::NotADirectory => reject_with_message(\"select the repository folder, not a file\"),\n    outcome => outcome.try_project(),\n}","preventionTips":["Require is_dir() before onboarding paths","Reject pasted URLs early with a 'clone first' hint instead of letting them reach add_project","Resolve symlinks with canonicalize so file symlinks fail the directory check"],"tags":["gitbutler","project","filesystem","validation"],"backgroundTag":"path-not-a-directory","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}