GitoxideLabs/gitoxide · error

a captured ref does not logically point into the rebase…

Error message

a captured ref does not logically point into the rebase scope

What it means

During rebase state validation, each recorded expected ref must point to a commit that is either inside the captured rebase scope, the base commit, or the onto target. If a ref's target is none of these, the state file is logically inconsistent (e.g. corrupted or hand-edited) and tix refuses to restore the rebase. This prevents the rebase from moving refs to commits unrelated to the in-progress operation.

Solutions

  1. Delete the stale rebase state and restart the rebase so state is regenerated from real refs
  2. Inspect the recorded expected_refs in the state file and correct ref targets to commits within the scope, base, or onto
  3. Check that no concurrent git/tix operation (reset, rebase, GC) modified the refs captured by the in-progress rebase

Example fix

// before (hand-edited state)
ref main = 1a2b3c4... (commit outside scope)
// after
abandon the state (`tix` restart the rebase) so expected_refs are recaptured from actual refs
Defensive patterns

Strategy: validation

Validate before calling

// before resuming, verify every expected ref target is in scope/base/onto
for reference in &state.expected_refs {
    assert!(scope.contains(&reference.target)
        || reference.target == state.base
        || reference.target == state.onto,
        "ref {} points outside rebase scope", reference.name);
}

Try / catch

match result {
    Err(e) if e.to_string().contains("does not logically point into the rebase scope") => {
        // discard state, prompt user to restart the rebase
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling the todo/state parsing path (parse -> parse_state -> validate_state) on a persisted rebase state whose `expected_refs` contain a reference whose target commit is outside `state.scope`, `state.base`, and `state.onto`. Typically caused by manual editing of the state file, corruption, or stale state after history was rewritten externally.

Common situations: Hand-editing or script-mangling the tix rebase state file; resuming a rebase after a GC or reset removed and rewrote commits so recorded ref targets no longer match; copying rebase state between repositories.

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


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/06744d3f1b8fd9f7. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/todo.rs:793

    repo.find_commit(state.base)
        .context("could not find the recorded rebase base")?;
    repo.find_commit(state.onto)
        .context("could not find the recorded rebase target")?;
    let scope: HashSet<_> = state.scope.iter().copied().collect();
    let continuation_sources: HashSet<_> = state.continuation_sources.iter().copied().collect();
    if scope.len() != state.scope.len() {
        anyhow::bail!("the rebase state contains duplicate scope commits");
    }
    if state.tips.iter().copied().collect::<HashSet<_>>().len() != state.tips.len() {
        anyhow::bail!("the rebase state contains duplicate tips");
    }
    let mut refs = HashSet::new();
    for reference in &state.expected_refs {
        if !refs.insert(reference.name.as_bstr()) {
            anyhow::bail!("the rebase state contains duplicate refs");
        }
        if !scope.contains(&reference.target) && reference.target != state.base && reference.target != state.onto {
            anyhow::bail!("a captured ref does not logically point into the rebase scope");
        }
    }
    if let Some(name) = &state.head_ref
        && !state
            .expected_refs
            .iter()
            .any(|reference| reference.editable && reference.name == *name)
    {
        anyhow::bail!("the recorded HEAD ref is not editable");
    }
    for tip in &state.tips {
        repo.find_commit(*tip).context("could not find a recorded rebase tip")?;
    }
    for id in &state.scope {
        let commit = repo
            .find_commit(*id)
            .context("could not find a recorded scope commit")?;
        let parent = commit

View on GitHub (pinned to e73179060b)