gitbutlerapp/gitbutler · error

Refusing to apply a reference that already is a workspace

Error message

Refusing to apply a reference that already is a workspace: '{}'

What it means

`Workspace::apply` refuses to apply a branch reference that is itself already a workspace root in the workspace metadata. A reference can only be either a workspace or an applied branch, not both, so applying it as a branch would corrupt the workspace structure. This is a deliberate precondition, not a transient failure.

Solutions

  1. Use the actual feature branch name, not the workspace branch, in the apply call
  2. If the branch should no longer be a workspace, remove its workspace registration in the metadata before applying
  3. Check `meta.workspace_opt(branch_ref)` before calling apply and skip or handle workspace references explicitly

Example fix

// before
ws.apply(meta, "my-workspace-branch", ...)?;
// after
if meta.workspace_opt(&branch)?.is_some() {
    bail!("{ } is a workspace, pick a stack branch instead");
}
ws.apply(meta, "feature-branch", ...)?;
Defensive patterns

Strategy: validation

Validate before calling

if meta.workspace_opt(&branch_ref)?.is_some() {
    return Err(format!("{} is a workspace; apply a regular branch instead", branch_ref.shorten()));
}

Type guard

fn is_workspace_branch(meta: &M, r: &gix::refs::Reference) -> bool {
    meta.workspace_opt(r).map(|o| o.is_some()).unwrap_or(false)
}

Try / catch

match apply(...) {
    Err(e) if e.to_string().contains("already is a workspace") => /* pick a stack branch instead */,
    Err(e) => return Err(e.into()),
    Ok(r) => r,
}

Prevention

When it happens

Trigger: Calling `but_workspace::branch::apply` (or higher-level `but apply`) with a branch name that `meta.workspace_opt(branch)` reports as an existing workspace, while that workspace commit is not at the top of its ancestry (the prior check passed).

Common situations: Users pass the workspace/meta branch name instead of a feature branch; a branch previously used as a workspace is re-applied after the workspace commit was removed from the tip; scripted apply loops iterating over all refs including workspace branches.

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/a908ba4ebe3da9ea. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-workspace/src/branch/apply.rs:362

        });
    };

    if let Some(ws_ref_name) = ws.ref_name()
        && repo.try_find_reference(ws_ref_name)?.is_none()
    {
        // The workspace is the probably ad-hoc, and doesn't exist, *assume* unborn.
        bail!(
            "Cannot create reference on unborn branch '{}'",
            ws_ref_name.shorten()
        );
    }

    if ws.has_workspace_commit_in_ancestry(repo) {
        bail!("Refusing to work on workspace whose workspace commit isn't at the top");
    }

    if meta.workspace_opt(branch.as_ref())?.is_some() {
        bail!(
            "Refusing to apply a reference that already is a workspace: '{}'",
            branch.shorten()
        );
    }
    // In general, we only have to deal with one branch to apply. But when we are on an adhoc workspace,
    // we need to assure both branches go into the existing or the new workspace:
    //  - the current one and the one to apply, if these are different.
    // The returned workspace ref name will be set to the new merge commit, if created, or it may not change
    // at all if the workspace can be created by just setting metadata.
    let (workspace_ref_name_to_update, branches_to_apply) = match &ws.kind {
        WorkspaceKind::Managed { ref_info }
        | WorkspaceKind::ManagedMissingWorkspaceCommit { ref_info } => {
            (ref_info.ref_name.clone(), vec![branch.clone()])
        }
        WorkspaceKind::AdHoc => {
            // We need to switch over to a possibly existing workspace.
            // We know that the current branch is *not* reachable from the workspace or isn't naturally included,
            // so it needs to be added as well.

View on GitHub (pinned to 58e5313667)