gitbutlerapp/gitbutler · error
' ' already exists
Error message
'{path}' already exists What it means
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.
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
Example fix
// before: blind add that can collide
add(&repo, &Path::new("../wt-feature"), branch, base)?;
// after: ensure the path is free first
let path = Path::new("../wt-feature");
if path.exists() { std::fs::remove_dir_all(path)?; } // or pick another name
add(&repo, path, branch, base)?; Defensive patterns
Strategy: validation
Validate before calling
let path = Path::new("../wt-feature");
anyhow::ensure!(!path.exists(), "worktree path {} already exists", path.display()); Try / catch
match add(&repo, &path, branch, base) {
Err(e) if e.to_string().contains("already exists") => pick_alternate_path_and_retry(),
r => r,
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- `but setup` cannot run from a linked worktree; run it from…
- Cannot currently work in repositories without a worktree
- Cannot currently work in repositories without a worktree
- Cannot discard lines in
- Cannot hash directory entries that aren't files or symlinks
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/4072de51584d4d2e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/worktrees.rs:113
args.push(OsStr::new("--force"));
}
args.extend([OsStr::new("--"), path.as_os_str()]);
git_worktree(repo, "remove", &args)
}
/// Create a linked worktree at `path` on the new branch `branch` starting at `base`, the way
/// `git worktree add -b` does, which refuses an existing branch.
///
/// An existing `path` is refused up front, as git would only notice it after creating the branch.
/// Returns the stable name git gave the worktree, normally the last component of `path`.
pub fn add(
repo: &gix::Repository,
path: &Path,
branch: &gix::refs::FullNameRef,
base: gix::ObjectId,
) -> anyhow::Result<BString> {
if path.exists() {
bail!("'{}' already exists", path.display());
}
let short_name = gix::path::from_bstr(branch.shorten());
let base = base.to_string();
git_worktree(
repo,
"add",
&[
OsStr::new("-b"),
short_name.as_os_str(),
OsStr::new("--"),
path.as_os_str(),
OsStr::new(&base),
],
)?;
gix::open(path)?
.worktree()
.and_then(|worktree| worktree.id().map(ToOwned::to_owned))
.context("git registered the new checkout as a linked worktree")View on GitHub (pinned to 58e5313667)