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
- Set a real virtual branch name, e.g. `Reference::Virtual("gitbutler/workspace".into())`
- Trace where the step was constructed and assert the name is non-empty at that boundary
- 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
- Construct Reference steps only from names obtained from the workspace branch API
- Assert non-empty names at the boundary where steps are built
- Skip emitting the Reference step when no name exists instead of defaulting one
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
- invalid range: {start}-{end}
- No rebase steps provided
- When using OpenRouter, you must provide a valid API key
- Invalid message format
- HTTP Error ${response.statusText}: ${text}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/02d979e3ad9071d2.
Report an issue: GitHub.