gitbutlerapp/gitbutler · error

Reference step must have a non-empty virtual branch name

Error message

Reference step must have a non-empty virtual branch name

What it means

`Rebase::steps()` validates each step as it is added; a `RebaseStep::Reference(but_core::Reference::Virtual(name))` with an empty `name` is rejected because a virtual-branch reference needs a branch name to point at. Reference steps create/update refs after the rebase, so an empty virtual name would produce an unusable reference. Non-virtual references (e.g. regular git refs) are not affected by this check.

Source

Thrown at crates/but-rebase/src/lib.rs:196

        match step {
            RebaseStep::Pick { commit_id, .. } => {
                self.assure_unique_step_and_existing_non_base(commit_id, "Picked")?;
            }
            RebaseStep::SquashIntoPreceding {
                commit_id,
                new_message: _,
            } => {
                self.assure_unique_step_and_existing_non_base(commit_id, "Fixup")?;
                if matches!(self.steps.last(), Some(RebaseStep::Reference { .. })) {
                    bail!("Fixup commit must not come after a reference step");
                }
                if self.steps.is_empty() {
                    bail!("Fixup must have a commit to work on");
                }
            }
            RebaseStep::Reference(name) => {
                if matches!(name, but_core::Reference::Virtual(name) if name.is_empty()) {
                    return Err(anyhow!(
                        "Reference step must have a non-empty virtual branch name"
                    ));
                }
            }
        }
        Ok(())
    }

    fn assure_unique_step_and_existing_non_base(
        &self,
        commit_id: &gix::oid,
        kind: &str,
    ) -> Result<()> {
        self.repo.find_commit(commit_id)?;
        if Some(commit_id) == self.base.as_deref() {
            bail!("{kind} commit cannot be the base commit");
        }
        if self.steps.iter().any(|s| s.commit_id() == Some(commit_id)) {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Set a real virtual branch name, e.g. `Reference::Virtual("gitbutler/workspace".into())`
  2. Trace where the step was constructed and assert the name is non-empty at that boundary
  3. Skip emitting the Reference step entirely when there is no name — usually the step itself is optional

Example fix

// before
rebase.steps([RebaseStep::Reference(but_core::Reference::Virtual(String::new()))])?; // Err

// after
rebase.steps([RebaseStep::Reference(but_core::Reference::Virtual(
    "gitbutler/workspace".into(),
))])?;
Defensive patterns

Strategy: validation

Validate before calling

if let RebaseStep::Reference(but_core::Reference::Virtual(name)) = &step {
    anyhow::ensure!(!name.is_empty(), "virtual branch name must not be empty");
}
rebase.steps([step])?;

Type guard

fn has_non_empty_virtual_name(step: &RebaseStep) -> bool {
    !matches!(step, RebaseStep::Reference(but_core::Reference::Virtual(name)) if name.is_empty())
}

Prevention

When it happens

Trigger: Pushing `RebaseStep::Reference(Reference::Virtual(String::new()))` or building the step from an unset workspace/virtual branch name field; defaults from `Default::default()`-style construction where the name was never filled in.

Common situations: Serialization round-trips that lose the branch name; UI flows that create a reference step for a workspace branch whose name failed to generate; test fixtures built with placeholder empty strings.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/02d979e3ad9071d2. Report an issue: GitHub.