GitoxideLabs/gitoxide · error

the rebase state has more than one onto target

Error message

the rebase state has more than one onto target

What it means

Like `base`, the `onto` key in the rebase state anchor is single-valued. A second `onto <oid>` line makes the rebase's target commit ambiguous, so `parse_state` refuses the state instead of silently choosing one.

Solutions

  1. Delete the extra `onto` line, keeping the correct target oid.
  2. Regenerate the state anchor via tix rather than editing it manually.
  3. Abort the rebase and restart to obtain a clean state block.
  4. Sanity-check the anchor: `grep -c '^onto '` must return 1.

Example fix

// before (inside anchor body)
// onto aaa111...
// onto bbb222...
let state = parse_state(repo, text)?; // bails

// after
// onto aaa111...   (single, correct onto)
let state = parse_state(repo, &dedupe_state_lines(text))?;
Defensive patterns

Strategy: validation

Validate before calling

let onto_lines = body.lines().filter(|l| l.starts_with("onto ")).count();
if onto_lines > 1 {
    anyhow::bail!("state anchor declares onto more than once");
}

Try / catch

match parse(repo, todo_text) {
    Ok(s) => s,
    Err(e) if e.to_string().contains("more than one onto target") => {
        // fix the anchor to a single onto line, then retry
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `parse` on a todo whose state anchor body lists `onto <oid>` more than once — duplicated state lines from hand edits, bad merges, or buggy generators.

Common situations: Manual anchor editing introducing a stray onto line; a merge/patch that duplicated the onto line inside the same anchor; scripted todo generation writing onto twice.

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

Appendix: source

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

    let mut scope = Vec::new();
    let mut marker_required = None;
    let mut checkout_allowed = None;
    let mut head_ref = None;
    let mut edit_refs = false;
    let mut expected_refs = Vec::new();
    let mut resolved = None;
    let mut continuation_sources = Vec::new();
    for line in body[..end].lines() {
        let (key, value) = line.split_once(' ').context("a rebase state line has no value")?;
        match key {
            "base" => {
                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)

View on GitHub (pinned to e73179060b)