{"record":{"id":"4072de51584d4d2e","repo":"gitbutlerapp/gitbutler","slug":"path-already-exists","errorCode":null,"errorMessage":"'{path}' already exists","messagePattern":"'(.+?)' already exists","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/worktrees.rs","lineNumber":113,"sourceCode":"        args.push(OsStr::new(\"--force\"));\n    }\n    args.extend([OsStr::new(\"--\"), path.as_os_str()]);\n    git_worktree(repo, \"remove\", &args)\n}\n\n/// Create a linked worktree at `path` on the new branch `branch` starting at `base`, the way\n/// `git worktree add -b` does, which refuses an existing branch.\n///\n/// An existing `path` is refused up front, as git would only notice it after creating the branch.\n/// Returns the stable name git gave the worktree, normally the last component of `path`.\npub fn add(\n    repo: &gix::Repository,\n    path: &Path,\n    branch: &gix::refs::FullNameRef,\n    base: gix::ObjectId,\n) -> anyhow::Result<BString> {\n    if path.exists() {\n        bail!(\"'{}' already exists\", path.display());\n    }\n    let short_name = gix::path::from_bstr(branch.shorten());\n    let base = base.to_string();\n    git_worktree(\n        repo,\n        \"add\",\n        &[\n            OsStr::new(\"-b\"),\n            short_name.as_os_str(),\n            OsStr::new(\"--\"),\n            path.as_os_str(),\n            OsStr::new(&base),\n        ],\n    )?;\n    gix::open(path)?\n        .worktree()\n        .and_then(|worktree| worktree.id().map(ToOwned::to_owned))\n        .context(\"git registered the new checkout as a linked worktree\")","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-workspace/src/worktrees.rs#L95-L131","documentation":"The worktree-add helper refuses to create a linked worktree at a filesystem path that already exists, because `git worktree add` would fail or, worse, write into an occupied directory. The check happens before invoking git.","triggerScenarios":"Calling worktrees::add(repo, path, branch, base) where path.exists() is true — the target directory is already present (occupied by a project, an empty folder, or a stale worktree directory left behind by a manual deletion).","commonSituations":"Re-running an idempotent setup script without pruning previous worktrees; stale directories left after `git worktree prune` (prune removes metadata but not stray files); a user- or IDE-created directory at the intended path; case-insensitive filesystems colliding with an existing name.","solutions":["Choose a fresh, non-existent path for the new worktree","Remove the existing directory if it is stale or empty before calling add","Run `git worktree prune` and manually delete leftover directories from removed worktrees","Check `path.exists()` (or list registered worktrees) before calling to pick a unique name"],"exampleFix":"// before: blind add that can collide\nadd(&repo, &Path::new(\"../wt-feature\"), branch, base)?;\n// after: ensure the path is free first\nlet path = Path::new(\"../wt-feature\");\nif path.exists() { std::fs::remove_dir_all(path)?; } // or pick another name\nadd(&repo, path, branch, base)?;","handlingStrategy":"validation","validationCode":"let path = Path::new(\"../wt-feature\");\nanyhow::ensure!(!path.exists(), \"worktree path {} already exists\", path.display());","typeGuard":null,"tryCatchPattern":"match add(&repo, &path, branch, base) {\n    Err(e) if e.to_string().contains(\"already exists\") => pick_alternate_path_and_retry(),\n    r => r,\n}","preventionTips":["Generate unique worktree directories (timestamp/suffix) instead of fixed names","Run `git worktree prune` and clean stray directories in setup scripts","Check path existence before calling add","Make setup scripts idempotent by removing stale worktree paths first"],"tags":["git","worktree","filesystem","path-collision"],"backgroundTag":"file-already-exists","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}