GitoxideLabs/gitoxide · error

the rebase state repeats checkout-allowed

Error message

the rebase state repeats checkout-allowed

What it means

Same duplicate-field protection as `marker-required`, but for the `checkout-allowed` key: `parse_state` encountered a second `checkout-allowed <bool>` line in the rebase state file. Each scalar field may appear only once; a repeat indicates a malformed state file and is rejected instead of silently overwriting the first value.

Solutions

  1. Deduplicate the `checkout-allowed` lines in the state file, keeping one.
  2. Regenerate the state by restarting the rebase/edit operation from scratch.
  3. Verify no concurrent process is writing to the state file while it is being parsed.

Example fix

// before (state file)
checkout-allowed true
checkout-allowed false
// after
checkout-allowed true
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

match todo::parse(state_text) {
    Ok(state) => apply(state),
    Err(e) if e.to_string().contains("repeats checkout-allowed") => {
        eprintln!("corrupt state: duplicate checkout-allowed; regenerate state");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `parse_state` (gix-tix/src/edit/todo.rs:702) reading a state file containing more than one `checkout-allowed` line; hand-edited, corrupted, or concurrently-written rebase state.

Common situations: Manual editing mistakes in a rebase state file; automation appending fields instead of replacing them; state files shared between two worktrees or interrupted tool runs.

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/d3fed122471645c6. Report an issue: GitHub.

Appendix: source

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

                if base.replace(ObjectId::from_hex(value.as_bytes())?).is_some() {
                    anyhow::bail!("the rebase state has more than one base");
                }
            }
            "onto" => {
                if onto.replace(ObjectId::from_hex(value.as_bytes())?).is_some() {
                    anyhow::bail!("the rebase state has more than one onto target");
                }
            }
            "tip" => tips.push(ObjectId::from_hex(value.as_bytes())?),
            "scope" => scope.push(ObjectId::from_hex(value.as_bytes())?),
            "marker-required" => {
                if marker_required.replace(value.parse()?).is_some() {
                    anyhow::bail!("the rebase state repeats marker-required");
                }
            }
            "checkout-allowed" => {
                if checkout_allowed.replace(value.parse()?).is_some() {
                    anyhow::bail!("the rebase state repeats checkout-allowed");
                }
            }
            "head-ref" => {
                let encoded = value.as_bytes().as_bstr();
                let (name, consumed) = gix::quote::ansi_c::undo(encoded)
                    .map_err(gix::Exn::into_error)
                    .context("could not unquote the recorded HEAD ref")?;
                if !encoded[consumed..].trim().is_empty() {
                    anyhow::bail!("the recorded HEAD ref has trailing data");
                }
                let name = gix::refs::FullName::try_from(name.as_ref()).context("the recorded HEAD ref is invalid")?;
                if head_ref.replace(name).is_some() {
                    anyhow::bail!("the rebase state repeats its HEAD ref");
                }
            }
            "edit-refs" => edit_refs = value.parse()?,
            "ref" => {
                let (old, value) = value.split_once(' ').context("a captured ref has no target")?;

View on GitHub (pinned to e73179060b)