GitoxideLabs/gitoxide · error
the root of a repeated rebase must be pending
Error message
the root of a repeated rebase must be pending
What it means
This error is thrown during rebase edit validation in gix-tix. When an operation repeats a rebase (the `repeat` flag with the edited commit at position 0, i.e. it is the root of the rebased range), the root commit must itself be in a 'pending' rebase state. If the root commit is not pending, the repeated rebase would start from a commit that is not part of an unfinished rebase, so the edit is rejected.
Solutions
- Ensure the root commit of the repeated rebase is still in pending state before invoking the edit, or omit `repeat`/start the range at position > 0.
- Re-create or refresh the pending rebase state so the root is pending again.
- Pick the correct root commit id for the in-progress rebase instead of a completed one.
- If the rebase is actually finished, perform a normal (non-repeated) rebase edit instead.
Example fix
// before: repeating a rebase from an already-completed root edit.rebase(root_of_finished_rebase, /* repeat */ true, ...)?; // after: verify the root is pending, else do a fresh rebase let commit = repo.find_commit(root_id)?.decode()?.into_owned()?; let repeat = gix_tix::edit::rebase::is_pending(&commit); edit.rebase(root_id, repeat, ...)?;
Defensive patterns
Strategy: validation
Validate before calling
let commit = repo.find_commit(root_id)?.decode()?.into_owned()?;
if !gix_tix::edit::rebase::is_pending(&commit) {
// either don't set repeat, or re-establish the pending state first
}
Type guard
fn is_repeatable_root(repo: &gix::Repository, id: ObjectId) -> anyhow::Result<bool> {
Ok(gix_tix::edit::rebase::is_pending(&repo.find_commit(id)?.decode()?.into_owned()?))
}
Prevention
- Check `is_pending` on the rebase root before any repeated edit
- Complete or abort pending rebases before running follow-up edits
- Never reuse root ids captured before a prior rebase — re-resolve them
When it happens
Trigger: Calling a rebase-edit operation with `repeat` enabled where the first commit in the edit range (position == 0, single-parent commit whose id is `*id`) decodes to a commit that `is_pending()` reports as NOT pending. This happens in the validation loop after the merge-commit check (parents.len() > 1 && (position > 0 || removed || tree == Tree::CherryPick)).
Common situations: Re-running a stored/repeated rebase script after the pending rebase was already completed, aborted, or its root was rewritten by an earlier edit; pointing the edit at the wrong (already-finalized) root commit.
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
- the parent of a repeated rebase must not be pending
- the current checkout has a pending rebase; time-travel to…
- has a pending rebase
- the rebase state contains duplicate scope commits
- the rebase state contains duplicate tips
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/8ea09b34a088c932.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/rebase.rs:2552
}
fn validate(
repo: &gix::Repository,
graph: &HistoryGraph,
affected: &[ObjectId],
removed: bool,
repeat: bool,
tree: Tree,
) -> Result<()> {
for (position, id) in affected.iter().enumerate() {
let parents = graph.parents_of(*id).context("an affected commit is incomplete")?;
if parents.len() > 1 && (position > 0 || removed || tree == Tree::CherryPick) {
anyhow::bail!("descendant merge commits cannot be rebased");
}
if repeat && position == 0 {
let commit = repo.find_commit(*id)?.decode()?.into_owned()?;
if !is_pending(&commit) {
anyhow::bail!("the root of a repeated rebase must be pending");
}
}
}
if repeat
&& let Some(base) = affected.first()
&& let Some(parent) = graph.parents_of(*base).and_then(|parents| parents.first().copied())
&& is_pending(&repo.find_commit(parent)?.decode()?.into_owned()?)
{
anyhow::bail!("the parent of a repeated rebase must not be pending");
}
Ok(())
}
fn reject_pending_checkout_path(repo: &gix::Repository, mut id: ObjectId) -> Result<()> {
let mut seen = HashSet::new();
while seen.insert(id) {
let commit = repo.find_commit(id)?.decode()?.into_owned()?;
if is_pending(&commit) {View on GitHub (pinned to e73179060b)