GitoxideLabs/gitoxide · error

the rebase todo contains more than one state anchor

Error message

the rebase todo contains more than one state anchor

What it means

A valid rebase todo must contain exactly one state anchor comment. After locating and closing the first anchor, `parse_state` checks whether another `STATE_START` occurs after it; a second anchor means the todo file is malformed (duplicated state blocks) and would make the state ambiguous, so parsing fails.

Solutions

  1. Open the todo file and delete the duplicate state anchor block, keeping exactly one.
  2. Restore the todo file from before the conflicting edit/merge.
  3. Regenerate the todo via tix's rebase preparation to get a clean single anchor.
  4. Abort the rebase and restart if the todo is unrecoverable.

Example fix

// before
// "<!-- tix-rebase-state-v2 base=... -->\n...<!-- tix-rebase-state-v2 base=... -->"
let state = parse_state(repo, text)?; // bails

// after
let cleaned = keep_first_anchor_only(text);
let state = parse_state(repo, &cleaned)?;
Defensive patterns

Strategy: validation

Validate before calling

let anchors = todo_text.matches("<!-- tix-rebase-state-").count();
if anchors != 1 {
    anyhow::bail!("expected exactly 1 state anchor, found {anchors}");
}

Try / catch

match parse(repo, todo_text) {
    Ok(s) => s,
    Err(e) if e.to_string().contains("more than one state anchor") => {
        // repair the todo (keep one anchor) and retry
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `parse`/`parse_state` on a todo string where the state anchor comment `<!-- tix-rebase-state-... -->` appears more than once — e.g. the file was concatenated, a merge conflict duplicated the block, or tooling appended a second state section.

Common situations: A merge of two branches both editing the todo file duplicated the anchor; an editor or script accidentally appended the state block twice; a user pasted a todo template containing an existing anchor.

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

Appendix: source

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

            .shorten()
            .context("could not shorten a rebase todo ID")?
            .to_string())
    }
}

fn parse_state(repo: &gix::Repository, input: &str) -> Result<Option<State>> {
    let Some(start) = input.find(STATE_START) else {
        if input.contains("<!-- tix-rebase-state-") {
            anyhow::bail!("the rebase todo uses an unsupported state version");
        }
        return Ok(None);
    };
    let body = &input[start + STATE_START.len()..];
    let end = body
        .find(STATE_CLOSE)
        .context("the rebase state anchor is not closed")?;
    if body[end + STATE_CLOSE.len()..].contains(STATE_START) {
        anyhow::bail!("the rebase todo contains more than one state anchor");
    }
    let mut base = None;
    let mut onto = None;
    let mut tips = Vec::new();
    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");

View on GitHub (pinned to e73179060b)