gitbutlerapp/gitbutler · error
Fixup must have a commit to work on
Error message
Fixup must have a commit to work on
What it means
A SquashIntoPreceding (fixup) step requires a preceding step to squash into. If self.steps is empty when the fixup is validated, there is no commit to fix up into, so validation rejects the plan. Note this check runs after the reference check, and assure_unique_step_and_existing_non_base already verified the fixup commit itself exists and is unique.
Solutions
- Add the target pick step before the fixup: call a step-adding method (e.g. push/commit step) first.
- If the fixup should start the plan, convert it into a normal pick instead of squash_into_preceding.
- Check plan-generation code so fixups are only emitted after their target commit step.
Example fix
// before rebase.squash_into_preceding(fixup_id, None)?; // empty plan -> fails // after rebase.push(base_commit_id)?; rebase.squash_into_preceding(fixup_id, None)?;
Defensive patterns
Strategy: validation
Validate before calling
fn has_step_to_fixup(steps: &[RebaseStep]) -> bool {
!steps.is_empty()
} Type guard
if steps.is_empty() { return Err(anyhow!("fixup requires a prior step")); } Prevention
- Never start a plan with a fixup step
- Assert plan non-emptiness before appending fixups
- In plan generators, always emit the target pick before its fixups
When it happens
Trigger: Calling squash_into_preceding (or building a plan whose first step is a fixup) on a Rebase with no prior steps — e.g. steps empty because base-only construction or prior steps were never added.
Common situations: Starting a plan with a fixup by mistake; logic that clears steps before re-adding them while fixups are queued; UI automation emitting a squash as the first action of a new rebase.
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
- Fixup commit must not come after a reference step
- Invalid parent delimitation: requested child is not a…
- Invalid parent delimitation: requested parent is not a…
- commit cannot be the base commit
- No rebase steps provided
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/9a83e30f53fb2382.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-rebase/src/lib.rs:191
/// - Must not be the first operation
///
/// Reference operations:
/// - The refname must be a valid reference name
fn validate_step(&self, step: &RebaseStep) -> Result<()> {
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<()> {View on GitHub (pinned to 58e5313667)