gitbutlerapp/gitbutler · error
`but merge` requires an active GitButler workspace…
Error message
`but merge` requires an active GitButler workspace (`gitbutler/workspace`). Switch into the workspace and try again.
What it means
`but merge` operates on a GitButler-managed workspace, which is identified by the `gitbutler/workspace` ref. `branch_land` checks that the loaded workspace has a managed ref and bails if the repository is not currently in an active (initialized) GitButler workspace.
Solutions
- Initialize/enter the GitButler workspace for the repository (e.g. `but init` / open the repo in the GitButler app) and retry
- Switch into the workspace so `gitbutler/workspace` exists, then re-run `but merge`
- If GitButler was uninstalled or the ref was deleted, re-create the workspace or fall back to a plain `git merge` into the target branch
Example fix
# before but merge feature-branch # error: requires an active GitButler workspace # after but init # or open in GitButler to create gitbutler/workspace but merge feature-branch
Defensive patterns
Strategy: validation
Validate before calling
if !repo.path().join("gitbutler/workspace").exists()
&& repo.find_reference("refs/gitbutler/workspace").is_err() {
println!("not in an active GitButler workspace; run `but init`");
return Ok(());
} Try / catch
match land_result {
Err(e) if e.to_string().contains("requires an active GitButler workspace") => {
initialize_workspace();
retry_land();
}
other => other?,
} Prevention
- Run `but init` (or open the repo in the GitButler app) before using `but` commands
- Check `but status` to confirm an active workspace before scripting merges
- Don't delete the `gitbutler/workspace` ref manually
When it happens
Trigger: Calling `but merge <branch>` in a repo where the workspace is not active — `ws.kind.has_managed_ref()` is false because `gitbutler/workspace` is absent or the workspace kind has no managed ref (e.g. plain git checkout, workspace switched off).
Common situations: Running the command in a plain Git repo never initialized with GitButler; after `but switch`-style operations that left the workspace; working in a bare or unrelated checkout.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Configured target branch has no branch name
- a committed transaction always materializes a workspace
- `but land` requires an active GitButler workspace…
- `but merge` requires an active GitButler workspace…
- Cannot
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/a2204b3c13b9da02.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-api/src/land/mod.rs:185
///
/// This fetches the target, lands the branch (fast-forward or signed merge commit, retrying when
/// the target moves underneath us), then reconciles the remaining applied branches onto the moved
/// target. The remote push is not undoable; see [`BranchLandResult::reconcile_skipped`] and the
/// workspace state for what to report.
#[but_api(napi, try_from = json::BranchLandResult)]
#[instrument(skip(ctx), err(Debug))]
pub fn branch_land(
ctx: &mut Context,
branch: String,
no_ff: bool,
whole_stack: bool,
) -> anyhow::Result<BranchLandResult> {
let base_branch = {
let mut guard = ctx.exclusive_worktree_access();
{
let (_repo, ws, _db) = ctx.workspace_and_db_with_perm(guard.read_permission())?;
if !ws.kind.has_managed_ref() {
bail!(
"`but merge` requires an active GitButler workspace (`gitbutler/workspace`). \
Switch into the workspace and try again."
);
}
}
crate::legacy::virtual_branches::get_base_branch_data(ctx, guard.write_permission())?
.ok_or_else(|| anyhow::anyhow!("No base branch configured"))?
};
let target_branch_name = base_branch.short_name.clone();
if target_branch_name.is_empty() {
bail!("Configured target branch has no branch name");
}
let fetch_remote_name = base_branch.remote_name.clone();
let push_remote_name = if base_branch.push_remote_name.is_empty() {
fetch_remote_name.clone()
} else {
base_branch.push_remote_name.clone()View on GitHub (pinned to 58e5313667)