GitoxideLabs/gitoxide · error
has a pending rebase
Error message
{label} has a pending rebase What it means
`review::start` refuses to begin a review when either the reviewed commit (`tip`) or the review `base` is currently part of a pending rebase: `super::rebase::is_pending(&commit)` is checked for both (`"reviewed commit"`, `"review base"`) and the failing label is interpolated into `{label} has a pending rebase`. Editing commits mid-rebase would produce unstable review content.
Solutions
- Wait for the rebase to complete, then start the review against the finalized commits.
- Abort the pending rebase and start the review on stable history.
- Choose a different base/tip pair outside the pending rebase range.
- Detect pending state up front and defer the review (e.g. re-run the trigger after the rebase finishes).
Example fix
// before: review during an in-progress rebase
review::start(repo, base_id, tip_id)?; // bails: "{label} has a pending rebase"
// after: defer until the commits are settled
for id in [base_id, tip_id] {
let c = repo.find_commit(id)?.decode()?.into_owned()?;
if rebase::is_pending(&c) { return Err(defer_review_until_rebase_done(id)); }
}
review::start(repo, base_id, tip_id)?; Defensive patterns
Strategy: validation
Validate before calling
for (label, id) in [("reviewed commit", tip_id), ("review base", base_id)] {
let c = repo.find_commit(id)?.decode()?.into_owned()?;
if gix_tix::edit::rebase::is_pending(&c) {
// defer review until the rebase involving {label} completes
}
}
Type guard
fn reviewable(repo: &gix::Repository, id: ObjectId) -> anyhow::Result<bool> {
Ok(!gix_tix::edit::rebase::is_pending(&repo.find_commit(id)?.decode()?.into_owned()?))
}
Prevention
- Gate review automation on rebase completion hooks
- Never start reviews while an interactive rebase is paused
- Re-check pending state of both endpoints immediately before review::start
When it happens
Trigger: Calling `review::start` where decoding `tip` or `base` yields a commit for which `rebase::is_pending` is true — i.e. either endpoint is a provisional commit of an in-progress rebase.
Common situations: Starting a review while an interactive rebase is paused; automation triggered on commits that are temporarily rewritten during a rebase; reviewing the base commit that itself is being rebased.
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
- review commits cannot be copied
- the root of a repeated rebase must be pending
- the parent of a repeated rebase must not be pending
- the current checkout has a pending rebase; time-travel to…
- the rebase state contains duplicate scope commits
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/15ee976e7a81cfe4.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/review.rs:128
) -> Result<Started> {
let repo = open_repository(repository_path, bare, false).context("could not open repository to start review")?;
let workdir = repo.workdir().context("review requires a worktree")?.to_owned();
let head = repo.head().context("could not read HEAD before review")?;
let restore = (
head.referent_name().map(ToOwned::to_owned),
head.id().map(gix::Id::detach),
);
if tip == base || !graph.is_ancestor(base, tip) {
anyhow::bail!("the review base must be an ancestor of the reviewed commit");
}
for (label, id) in [("reviewed commit", tip), ("review base", base)] {
let commit = repo
.find_commit(id)
.with_context(|| format!("could not find {label}"))?
.decode()?
.into_owned()?;
if super::rebase::is_pending(&commit) {
anyhow::bail!("{label} has a pending rebase");
}
}
ensure_clean(&workdir)?;
let departure_pin = match restore.1 {
Some(id) => {
let target = restore.0.clone().map_or(Target::Object(id), Target::Symbolic);
Some((
super::time_travel::create_pin(&repo, target, id, "tix review departure")?,
true,
))
}
None => None,
};
let name = next_reference(&repo)?;
let mut commit = gix::objs::Commit {
tree: repo.find_commit(base)?.tree_id()?.detach(),View on GitHub (pinned to e73179060b)