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

  1. Initialize/enter the GitButler workspace for the repository (e.g. `but init` / open the repo in the GitButler app) and retry
  2. Switch into the workspace so `gitbutler/workspace` exists, then re-run `but merge`
  3. 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

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


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)