gitbutlerapp/gitbutler · error

Could not delete branch '{}'

Error message

Could not delete branch '{}'

What it means

Mid-rename failure in but-api's branch rename: a backup recovery ref was created, then deleting the old reference failed; the original gix delete error is wrapped with 'Could not delete branch <ref>' and the backup ref is cleaned up (its own cleanup failure is only warned about). The new ref is never created, so the branch keeps its old name — the rename aborts with state restored.

Source

Thrown at crates/but-api/src/branch.rs:1372

                )
                .with_context(|| {
                    format!(
                        "Could not create recovery ref before renaming '{}'",
                        ref_name.as_bstr()
                    )
                })?,
            );

            if let Err(delete_err) = old_reference.delete() {
                if let Some(backup) = backup_reference.take()
                    && let Err(err) = backup.delete()
                {
                    warn!(
                        ?err,
                        "failed to remove branch-rename recovery ref after source deletion failed"
                    );
                }
                return Err(anyhow::Error::new(delete_err)
                    .context(format!("Could not delete branch '{}'", ref_name.as_bstr())));
            }
            if let Err(create_err) = repo.reference(
                new_ref.as_ref(),
                target_id,
                PreviousValue::MustNotExist,
                "rename branch",
            ) {
                let create_err = anyhow::Error::new(create_err)
                    .context(format!("Could not create branch '{}'", new_ref.as_bstr()));
                match repo.reference(
                    ref_name.as_ref(),
                    target_id,
                    PreviousValue::MustNotExist,
                    "restore branch after failed rename",
                ) {
                    Ok(_) => {
                        if let Some(backup) = backup_reference.take()

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Check for .git/refs/heads/<name>.lock and remove it if no git process is running.
  2. Retry after the competing git operation finishes; verify with `git branch --list`.
  3. Check filesystem permissions on .git/refs and .git/packed-refs.
  4. If it recurs, capture the full anyhow chain ({:#}) to see the underlying gix error.

Example fix

// before
rename_branch(&ctx, &old_name, &new_name)?;

// after: clear a stale lock first
let lock = repo.git_dir().join(format!("refs/heads/{}.lock", old_short_name));
if lock.exists() && !any_git_process_alive() {
    std::fs::remove_file(&lock)?;
}
rename_branch(&ctx, &old_name, &new_name)?;
Defensive patterns

Strategy: retry

Validate before calling

let lock = repo.git_dir().join(format!("refs/heads/{}.lock", old_short_name));
if lock.exists() {
    // another op is in flight or a stale lock survived a crash; resolve before renaming
}

Try / catch

for attempt in 0..2 {
    match rename_branch(&ctx, &old_name, &new_name) {
        Err(e) if attempt == 0 && e.to_string().contains("Could not delete branch") => {
            clean_stale_locks(&repo); // then retry once
            continue;
        }
        r => break r,
    }
}?

Prevention

When it happens

Trigger: gix delete_reference failing on refs/heads/<name>: a stale refs/heads/<name>.lock from a crashed git process, packed-refs write contention, filesystem permission errors, or a concurrent process (another git or GitButler instance) mutating the same ref.

Common situations: Two tools renaming/deleting the same branch simultaneously; leftover .lock files after a crash; read-only mounts; antivirus briefly locking files on Windows.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/e38807d4a356a848. Report an issue: GitHub.