gitbutlerapp/gitbutler · error · anyhow::Error
HEAD is detached after switching branches
Error message
HEAD is detached after switching branches
What it means
After branch_checkout_with_perm switches branches, the CLI reads the symbolic HEAD name to report it. repo.head_name() returns None when HEAD is detached; since a successful switch to a branch should leave HEAD symbolic, this error signals the checkout ended in detached HEAD.
Source
Thrown at crates/but/src/command/switch.rs:53
.ok_or_else(|| anyhow::anyhow!("BUG: clap requires target, --workspace, or --new"))?;
let branch = {
let repo = ctx.repo.get()?;
let id_map = IdMap::new_from_context(ctx, guard.read_permission())?;
target.resolve_existing_local_branch(&repo, &id_map)?
};
but_api::branch::branch_checkout_with_perm(ctx, branch.clone(), guard.write_permission())?;
if let Some(out) = out.for_human() {
writeln!(out, "Switched to branch '{}'", branch.shorten())?;
}
Ok(())
}
fn current_head_short_name(ctx: &but_ctx::Context) -> CliResult<String> {
let repo = ctx.repo.get()?;
let head_name = repo
.head_name()?
.ok_or_else(|| anyhow::anyhow!("HEAD is detached after switching branches"))?;
Ok(head_name.shorten().to_string())
}
View on GitHub (pinned to caf1f223d3)
Solutions
- Reattach HEAD with `git switch <branch>` (or `git switch -`) and retry `but switch` with a local branch name
- Switch to a local branch, not a remote ref or commit id
- Finish or abort any in-progress operation first: `git rebase --abort`, `git bisect reset`
Example fix
# before but switch origin/feature # detaches HEAD -> error afterwards # after git switch -c feature origin/feature # create a local branch first but switch feature
Defensive patterns
Strategy: validation
Validate before calling
let name = requested_branch.trim();
anyhow::ensure!(
repo.find_reference(format!("refs/heads/{name}")).is_ok(),
"'{name}' is not a local branch; switching would detach HEAD"
); Try / catch
On this error, recover with `git switch -` to return to the previous branch, then retry using a local branch name.
Prevention
- Only switch to local branch names
- Create a local branch for remote refs before switching
- Avoid concurrent HEAD-mutating git commands while but runs
When it happens
Trigger: Switching to something that is not a local branch (a remote-tracking ref or commit-ish detaches HEAD), switching while a rebase/bisect is in progress, or the target branch being removed by another process between listing and switching.
Common situations: `but switch origin/feature` detaching HEAD; concurrent git operations in another terminal; a repo left in detached HEAD by a prior tool.
Related errors
- When using OpenRouter, you must provide a valid API key
- Branch '{ref_name}' has no tracking branch
- Could not turn {name:?} into a valid reference name
- Not currently on a gitbutler/* branch.
- GitButler mode exit required: please run `but teardown` to p
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/e4a92b3a234de88b.
Report an issue: GitHub.