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

  1. Pick a different branch name or check existence first before calling create
  2. Use the no-name variant of the API, which generates a unique canned refname automatically
  3. If the existing branch is stale and safe to drop, delete it first, then create
  4. 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

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


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)