GitoxideLabs/gitoxide · error

the rebase state repeats its resolved conflict

Error message

the rebase state repeats its resolved conflict

What it means

The `resolved` field records the OID of a resolved conflict and is single-valued. `parse_state` uses `resolved.replace(...)` and bails when it already held a value, i.e. the state file contains a second `resolved` line. This enforces the one-occurrence rule for scalar state fields.

Solutions

  1. Keep exactly one `resolved <oid>` line, pointing at the most recent resolution.
  2. Delete the `resolved` line if no resolution should be recorded, then continue the operation.
  3. Restart the operation to regenerate consistent state.

Example fix

// before (state file)
resolved 0123abc...
resolved 4567def...
// after
resolved 4567def...
Defensive patterns

Strategy: validation

Validate before calling

fn state_file_is_clean(text: &str) -> bool {
    text.lines().filter(|l| l.starts_with("resolved ")).count() <= 1
}

Try / catch

match todo::parse(state_text) {
    Ok(state) => apply(state),
    Err(e) if e.to_string().contains("repeats its resolved conflict") => {
        eprintln!("corrupt state: duplicate resolved; keep the latest OID only");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `parse_state` (gix-tix/src/edit/todo.rs:750) reading a state file with two `resolved <oid>` lines — from hand edits, corrupted state, or appending automation.

Common situations: Users manually editing conflict-resolution state; scripts appending instead of replacing the OID after a new resolution; state files merged from divergent worktrees.

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


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

Appendix: source

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

                    .map_err(gix::Exn::into_error)
                    .context("could not unquote a captured ref name")?;
                if !encoded_name[consumed..].trim().is_empty() {
                    anyhow::bail!("a captured ref name has trailing data");
                }
                let name = gix::refs::FullName::try_from(name.as_ref()).context("a captured ref name is invalid")?;
                expected_refs.push(rebase::ExpectedRef {
                    name,
                    old,
                    target,
                    new: old,
                    follows_tip,
                    editable,
                    placement: None,
                });
            }
            "resolved" => {
                if resolved.replace(ObjectId::from_hex(value.as_bytes())?).is_some() {
                    anyhow::bail!("the rebase state repeats its resolved conflict");
                }
            }
            "continuation-source" => continuation_sources.push(ObjectId::from_hex(value.as_bytes())?),
            _ => anyhow::bail!("unsupported rebase state field {key:?}"),
        }
    }
    let state = State {
        base: base.context("the rebase state has no base")?,
        onto: onto.context("the rebase state has no onto target")?,
        tips,
        scope,
        marker_required: marker_required.context("the rebase state has no marker requirement")?,
        checkout_allowed: checkout_allowed.context("the rebase state has no checkout capability")?,
        head_ref,
        edit_refs,
        expected_refs,
        resolved,
        continuation_sources,

View on GitHub (pinned to e73179060b)