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

  1. Reattach HEAD with `git switch <branch>` (or `git switch -`) and retry `but switch` with a local branch name
  2. Switch to a local branch, not a remote ref or commit id
  3. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/e4a92b3a234de88b. Report an issue: GitHub.