GitoxideLabs/gitoxide · error
the rebase state contains duplicate refs
Error message
the rebase state contains duplicate refs
What it means
`validate_state` inserts every `expected_refs[].name` into a `HashSet` of ref names; if insertion fails, two captured refs share the same fully-qualified name. Since each expected ref entry describes a distinct reference update, duplicate names are ambiguous and validation aborts.
Solutions
- Merge or remove duplicate `ref` lines so each fully-qualified ref name appears exactly once.
- Regenerate the state by restarting the rebase/edit operation.
- Ensure automation keys ref entries by name and updates in place rather than appending.
Example fix
// before (state file) ref aaa fast-forward "refs/heads/wip" ref bbb fast-forward "refs/heads/wip" // after ref bbb fast-forward "refs/heads/wip"
Defensive patterns
Strategy: validation
Validate before calling
fn ref_lines_are_unique(text: &str) -> bool {
let names: Vec<&str> = text.lines()
.filter(|l| l.starts_with("ref "))
.map(|l| l.rsplit_once(' ').map(|(_, n)| n).unwrap_or(""))
.collect();
names.iter().collect::<std::collections::HashSet<_>>().len() == names.len()
} Try / catch
match todo::parse(state_text) {
Ok(state) => apply(state),
Err(e) if e.to_string().contains("duplicate refs") => {
eprintln!("merge duplicate ref entries; each ref name may appear once");
}
Err(e) => return Err(e),
} Prevention
- Key captured-ref records by fully-qualified ref name and update in place.
- Validate uniqueness of ref names before writing the state file.
- Round-trip validate generated state in automation before consuming it.
When it happens
Trigger: `parse_state` (via validate_state, gix-tix/src/edit/todo.rs:790) parses a state file where two `ref` lines (or an edit-ref/expected-ref combination) produce entries with identical `FullName`s.
Common situations: Hand-edited ref lists; scripts writing one `ref` line per change instead of updating the existing entry; state files merged 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 scope commits
- the rebase state contains duplicate tips
- 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/2ee424076931f105.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/todo.rs:790
}
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)
{
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 = repoView on GitHub (pinned to e73179060b)