{"record":{"id":"e4a92b3a234de88b","repo":"gitbutlerapp/gitbutler","slug":"head-is-detached-after-switching-branches","errorCode":null,"errorMessage":"HEAD is detached after switching branches","messagePattern":"HEAD is detached after switching branches","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but/src/command/switch.rs","lineNumber":53,"sourceCode":"        .ok_or_else(|| anyhow::anyhow!(\"BUG: clap requires target, --workspace, or --new\"))?;\n    let branch = {\n        let repo = ctx.repo.get()?;\n        let id_map = IdMap::new_from_context(ctx, guard.read_permission())?;\n        target.resolve_existing_local_branch(&repo, &id_map)?\n    };\n    but_api::branch::branch_checkout_with_perm(ctx, branch.clone(), guard.write_permission())?;\n\n    if let Some(out) = out.for_human() {\n        writeln!(out, \"Switched to branch '{}'\", branch.shorten())?;\n    }\n    Ok(())\n}\n\nfn current_head_short_name(ctx: &but_ctx::Context) -> CliResult<String> {\n    let repo = ctx.repo.get()?;\n    let head_name = repo\n        .head_name()?\n        .ok_or_else(|| anyhow::anyhow!(\"HEAD is detached after switching branches\"))?;\n    Ok(head_name.shorten().to_string())\n}\n","sourceCodeStart":35,"sourceCodeEnd":56,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/command/switch.rs#L35-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"`but switch origin/feature` detaching HEAD; concurrent git operations in another terminal; a repo left in detached HEAD by a prior tool.","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`"],"exampleFix":"# before\nbut switch origin/feature   # detaches HEAD -> error afterwards\n# after\ngit switch -c feature origin/feature   # create a local branch first\nbut switch feature","handlingStrategy":"validation","validationCode":"let name = requested_branch.trim();\nanyhow::ensure!(\n    repo.find_reference(format!(\"refs/heads/{name}\")).is_ok(),\n    \"'{name}' is not a local branch; switching would detach HEAD\"\n);","typeGuard":null,"tryCatchPattern":"On this error, recover with `git switch -` to return to the previous branch, then retry using a local branch name.","preventionTips":["Only switch to local branch names","Create a local branch for remote refs before switching","Avoid concurrent HEAD-mutating git commands while but runs"],"tags":["switch","detached-head","branch","git"],"backgroundTag":"detached-head","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}