GitoxideLabs/gitoxide · error
the rebase state contains duplicate scope commits
Error message
the rebase state contains duplicate scope commits
What it means
During state validation, `validate_state` collects all `scope` OIDs into a `HashSet` and compares its size against the original vector. A size mismatch means the same commit OID was listed in `scope` more than once; since scope membership is a set concept, duplicates indicate a malformed state and validation aborts.
Solutions
- Deduplicate `scope` lines in the state file so each commit OID appears once.
- Regenerate the state by restarting the rebase/edit operation.
- Fix any automation that builds the scope list to deduplicate OIDs before writing.
Example fix
// before (state file) scope 0123abc... scope 0123abc... // after scope 0123abc...
Defensive patterns
Strategy: validation
Validate before calling
fn scope_lines_are_unique(text: &str) -> bool {
let ids: Vec<&str> = text.lines()
.filter_map(|l| l.strip_prefix("scope "))
.collect();
ids.iter().collect::<std::collections::HashSet<_>>().len() == ids.len()
} Try / catch
match todo::parse(state_text) {
Ok(state) => apply(state),
Err(e) if e.to_string().contains("duplicate scope commits") => {
eprintln!("deduplicate scope lines before parsing");
}
Err(e) => return Err(e),
} Prevention
- Deduplicate OIDs before writing scope lines in generated state.
- Use a set-based collector when building the scope list.
- Round-trip validate generated state (write then parse) in tests.
When it happens
Trigger: `parse_state` (via validate_state, gix-tix/src/edit/todo.rs:782) parses a state file where two or more `scope <oid>` lines repeat the same commit OID.
Common situations: Hand-edited state duplicating a scope entry; scripts appending scope commits without deduplication; merged state files from divergent sessions.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- the rebase state contains duplicate tips
- the rebase state contains duplicate refs
- 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…
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/67111b15d6c84131.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/todo.rs:782
head_ref,
edit_refs,
expected_refs,
resolved,
continuation_sources,
};
validate_state(repo, &state)?;
Ok(Some(state))
}
fn validate_state(repo: &gix::Repository, state: &State) -> Result<()> {
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)View on GitHub (pinned to e73179060b)