gitbutlerapp/gitbutler · error
Branch ' ' already exists
Error message
Branch '{}' already exists What it means
Thrown when creating a branch with an explicitly requested name: after normalizing the short name and expanding it to a full `refs/heads/...` name, the reference already exists in the repository, so creation would silently overwrite or conflict. The existing full refname is included in the message.
Solutions
- Pick a different branch name or check existence first before calling create
- Use the no-name variant of the API, which generates a unique canned refname automatically
- If the existing branch is stale and safe to drop, delete it first, then create
- Check the exact name in the message — normalization may have altered your input
Example fix
// before
await api.createBranch({ name: "feature" }); // bails if refs/heads/feature exists
// after
if (!(await api.branchExists("feature"))) {
await api.createBranch({ name: "feature" });
} else {
await api.createBranch({}); // unique canned name
} Defensive patterns
Strategy: validation
Validate before calling
const full = `refs/heads/${name}`;
if (await api.refExists(full)) throw new Error(`branch ${full} already exists`); Type guard
function canCreateBranch(name: string, existing: string[]): boolean {
return !existing.map(b => b.replace("refs/heads/", "")).includes(name);
} Try / catch
try {
await api.createBranch({ name });
} catch (e) {
if (String(e).includes("already exists")) {
await api.createBranch({}); // fall back to unique canned name
} else { throw e; }
} Prevention
- Check ref existence before creating a named branch
- Make create scripts idempotent (create-or-skip)
- Omit the name to let the API generate a unique canned refname
- Beware case-insensitive filesystems when choosing names
When it happens
Trigger: Calling the create-branch API with `name` set to a branch that already resolves via `repo.try_find_reference` — same-name branch exists locally, or the normalized name collides with an existing ref.
Common situations: Re-running a script that creates branches without idempotency; case-insensitive filesystem collisions between differently-cased names; UI retry after a partially-completed create; normalization making `feature/x` collide with an existing ref.
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
- Cannot rename to ' ': a branch with that name already exists
- Branch ' ' not found
- Branch ' ' not found when checking for conflicts
- Branch ' ' cannot be created: the target commit ( ) already…
- Branch name ' ' collides with existing branch
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/a54299fb6ddd7f2b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-api/src/branch.rs:1587
branch_checkout_new_with_perm(ctx, name, guard.write_permission())
}
/// Creates a new local branch at the project target SHA and checks it out under
/// caller-held exclusive repository access.
pub fn branch_checkout_new_with_perm(
ctx: &mut but_ctx::Context,
name: Option<String>,
perm: &mut RepoExclusive,
) -> anyhow::Result<BranchCheckoutResult> {
let target_commit_id = ctx.project_meta()?.target_commit_id_or_err()?;
let branch = {
let repo = ctx.repo.get()?;
let branch = match name {
Some(name) => {
let normalized = but_core::branch::normalize_short_name(name.as_str())?;
let branch = gix::refs::Category::LocalBranch.to_full_name(normalized.as_bstr())?;
if repo.try_find_reference(branch.as_ref())?.is_some() {
bail!("Branch '{}' already exists", branch.as_bstr());
}
branch
}
None => unique_canned_refname(&repo)?,
};
repo.reference(
branch.as_ref(),
target_commit_id,
PreviousValue::MustNotExist,
"branch checkout new",
)
.with_context(|| format!("Could not create branch '{}'", branch.as_bstr()))?;
branch
};
branch_checkout_with_perm_only(ctx, branch, perm)
}View on GitHub (pinned to 58e5313667)