{"record":{"id":"e38807d4a356a848","repo":"gitbutlerapp/gitbutler","slug":"could-not-delete-branch","errorCode":null,"errorMessage":"Could not delete branch '{}'","messagePattern":"Could not delete branch '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/branch.rs","lineNumber":1372,"sourceCode":"                )\n                .with_context(|| {\n                    format!(\n                        \"Could not create recovery ref before renaming '{}'\",\n                        ref_name.as_bstr()\n                    )\n                })?,\n            );\n\n            if let Err(delete_err) = old_reference.delete() {\n                if let Some(backup) = backup_reference.take()\n                    && let Err(err) = backup.delete()\n                {\n                    warn!(\n                        ?err,\n                        \"failed to remove branch-rename recovery ref after source deletion failed\"\n                    );\n                }\n                return Err(anyhow::Error::new(delete_err)\n                    .context(format!(\"Could not delete branch '{}'\", ref_name.as_bstr())));\n            }\n            if let Err(create_err) = repo.reference(\n                new_ref.as_ref(),\n                target_id,\n                PreviousValue::MustNotExist,\n                \"rename branch\",\n            ) {\n                let create_err = anyhow::Error::new(create_err)\n                    .context(format!(\"Could not create branch '{}'\", new_ref.as_bstr()));\n                match repo.reference(\n                    ref_name.as_ref(),\n                    target_id,\n                    PreviousValue::MustNotExist,\n                    \"restore branch after failed rename\",\n                ) {\n                    Ok(_) => {\n                        if let Some(backup) = backup_reference.take()","sourceCodeStart":1354,"sourceCodeEnd":1390,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-api/src/branch.rs#L1354-L1390","documentation":"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.","triggerScenarios":"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.","commonSituations":"Two tools renaming/deleting the same branch simultaneously; leftover .lock files after a crash; read-only mounts; antivirus briefly locking files on Windows.","solutions":["Check for .git/refs/heads/<name>.lock and remove it if no git process is running.","Retry after the competing git operation finishes; verify with `git branch --list`.","Check filesystem permissions on .git/refs and .git/packed-refs.","If it recurs, capture the full anyhow chain ({:#}) to see the underlying gix error."],"exampleFix":"// before\nrename_branch(&ctx, &old_name, &new_name)?;\n\n// after: clear a stale lock first\nlet lock = repo.git_dir().join(format!(\"refs/heads/{}.lock\", old_short_name));\nif lock.exists() && !any_git_process_alive() {\n    std::fs::remove_file(&lock)?;\n}\nrename_branch(&ctx, &old_name, &new_name)?;","handlingStrategy":"retry","validationCode":"let lock = repo.git_dir().join(format!(\"refs/heads/{}.lock\", old_short_name));\nif lock.exists() {\n    // another op is in flight or a stale lock survived a crash; resolve before renaming\n}","typeGuard":null,"tryCatchPattern":"for attempt in 0..2 {\n    match rename_branch(&ctx, &old_name, &new_name) {\n        Err(e) if attempt == 0 && e.to_string().contains(\"Could not delete branch\") => {\n            clean_stale_locks(&repo); // then retry once\n            continue;\n        }\n        r => break r,\n    }\n}?","preventionTips":["Serialize branch mutations in the app; avoid concurrent renames.","Clean up .lock files when detecting crashed prior runs.","On Windows, expect transient antivirus locks and retry with backoff."],"tags":["git","refs","branch-rename","filesystem","locking"],"backgroundTag":"git-ref-update-failed","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}