{"record":{"id":"cbed589739afb545","repo":"gitbutlerapp/gitbutler","slug":"cannot-unapply-a-branch-from-an-ad-hoc-detached-wo","errorCode":null,"errorMessage":"Cannot unapply a branch from an ad-hoc detached workspace","messagePattern":"Cannot unapply a branch from an ad-hoc detached workspace","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/unapply.rs","lineNumber":252,"sourceCode":"\n        let branch_in_ws = ws.find_segment_and_stack_by_refname(branch);\n        if branch_in_ws.is_none() {\n            if branch_ref.is_none() {\n                bail!(\n                    \"Cannot unapply non-existing branch '{branch}'\",\n                    branch = branch.shorten()\n                );\n            }\n            // The branch exists in Git, but does not in the workspace: Nothing to do.\n            return Ok(Outcome::new(Cow::Borrowed(workspace)));\n        }\n        let branch_stack_was_entrypoint = branch_in_ws\n            .is_some_and(|(stack, _)| stack.segments.iter().any(|segment| segment.is_entrypoint));\n        let workspace_tip_was_entrypoint = ws.is_entrypoint();\n\n        let Some(workspace_ref_name) = workspace_ref_name else {\n            // This is an ad-hoc workspace by merit of being unnamed.\n            bail!(\"Cannot unapply a branch from an ad-hoc detached workspace\");\n        };\n        let mut ws_md = meta.workspace(workspace_ref_name.as_ref())?;\n        if ws.kind.has_managed_ref() || ws.has_metadata() {\n            ws.reconcile_metadata(&mut ws_md)?;\n        }\n        let branch_removed_from_ws_meta = ws_md.unapply_branch(branch);\n        if !branch_removed_from_ws_meta {\n            // The branch wasn't in workspace metadata, yet it was present, so also delete its branch metadata\n            // as it could be used to disambiguate the segment.\n            // TODO: this will actually be observable even if it doens't work, unless it's run in a transaction, which right now it's not!\n            //       Should be able to redo the traversal with an overlay that hides branch metadata, but I'd say it's not important enough.\n            meta.remove(branch)?;\n            let graph = ws\n                .graph\n                .redo_traversal_with_overlay(repo, meta, Overlay::default())?;\n            let workspace = graph.into_workspace()?;\n            if workspace.refname_is_segment(branch) {\n                bail!(","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/unapply.rs#L234-L270","documentation":"unapply needs a workspace reference name to update workspace metadata, but ws.ref_name() returned None — this is a detached, unnamed (ad-hoc) workspace with no ref to hang metadata or ref updates on. Unapply fundamentally requires a named workspace to record its effects, so it refuses.","triggerScenarios":"Calling unapply on a workspace whose kind is ad-hoc and which carries no reference name (anonymous entrypoint, e.g. HEAD detached or an unborn workspace ref).","commonSituations":"Repository opened while HEAD is detached; workspace ref (refs/heads/<workspace>) deleted outside GitButler; fresh repo with no commits where the workspace ref is unborn.","solutions":["Put the workspace on a named reference first (create/re-checkout the workspace ref), then retry.","If HEAD is detached, attach it to a branch before doing workspace operations.","For workspaces you want gone, delete the workspace ref and metadata explicitly rather than unapplying branches."],"exampleFix":"// before\nunapply(repo, meta, &ws, branch, opts)?;\n\n// after\nensure!(ws.ref_name().is_some(), \"unnamed/detached workspace: attach a workspace ref first\");\nunapply(repo, meta, &ws, branch, opts)?;","handlingStrategy":"validation","validationCode":"if ws.ref_name().is_none() {\n    // detached/unnamed workspace: attach a workspace ref before unapply\n    return attach_workspace_ref_and_retry();\n}","typeGuard":"fn workspace_is_named(ws: &but_graph::Workspace) -> bool {\n    ws.ref_name().is_some()\n}","tryCatchPattern":"match unapply(repo, meta, &ws, branch, opts) {\n    Err(err) if err.to_string().contains(\"ad-hoc detached workspace\") => {\n        Err(anyhow!(\"attach HEAD to a branch / create the workspace ref first\"))\n    }\n    other => other,\n}","preventionTips":["Never run workspace mutations while HEAD is detached.","Recreate a deleted workspace ref (refs/heads/<workspace>) before unapplying.","Check ws.ref_name() as a precondition in automation wrappers."],"tags":["gitbutler","unapply","detached-head","workspace","rust"],"backgroundTag":"detached-unnamed-workspace","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}