GitoxideLabs/gitoxide · error

a reference is placed more than once

Error message

a reference is placed more than once

What it means

Each reference name may be positioned (moved to a commit) at most once within a rebase todo. If the same ref appears in reference lines under two different fork/command sections, the parser cannot decide a single new target for it and bails.

Solutions

  1. Remove the duplicate reference line, keeping only the one in the desired section
  2. If the ref should not move at all, delete both lines and let tix keep it at its current position
  3. Reopen the todo to regenerate a clean template before re-editing

Example fix

// before
──fork abc1234──
(main)
──fork def5678──
(main)
// after
──fork abc1234──
(main)
──fork def5678──
(no ref line for main)
Defensive patterns

Strategy: validation

Validate before calling

let mut seen = HashSet::new();
for name in ref_names_in_todo(edited) {
    if !seen.insert(name.clone()) {
        return Err(format!("ref {} appears more than once", name));
    }
}

Try / catch

if let Err(e) = parse(repo, edited) {
    if e.to_string().contains("placed more than once") {
        // highlight the duplicated ref line for the user
    }
}

Prevention

When it happens

Trigger: `parse` bails when `ref_targets.insert(name, target)` returns Some — i.e. `parse_ref_line` yielded a ref whose name was already recorded for a different (or same) position. Happens when a user duplicates a `(refs/heads/...)` line across fork sections while editing the todo.

Common situations: Copy-pasting reference lines while rearranging sections; forgetting that moving a ref line relocates it (the original must be removed); template mistakes.

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

Appendix: source

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

            )?;
            cursor = Some(if let Some(index) = picked.get(&id) {
                rebase::PlanParent::Step(*index)
            } else if scope.contains(&id) {
                anyhow::bail!("a fork target must be picked before it is used");
            } else {
                rebase::PlanParent::Existing(id)
            });
            sections += 1;
            section_has_commit = false;
            section_last_step = None;
            continue;
        }
        if line.starts_with('(') && line.ends_with(')') {
            let target = cursor.context("a reference line must follow a fork or command")?;
            for (marked, value) in parse_ref_line(line)? {
                let name = resolve_ref_name(repo, &mut state.expected_refs, value.as_bstr())?;
                if ref_targets.insert(name.clone(), target).is_some() {
                    anyhow::bail!("a reference is placed more than once");
                }
                if marked {
                    if !state.checkout_allowed || repo.workdir().is_none() {
                        anyhow::bail!("the rebase todo cannot select a checkout without a worktree");
                    }
                    if explicit_checkout_reference.replace((name, target)).is_some() {
                        anyhow::bail!("the rebase todo contains more than one @ reference");
                    }
                }
            }
            section_has_commit = true;
            continue;
        }

        let (command, tail) = if let Some(line) = line.strip_prefix('`') {
            let (command, tail) = line
                .split_once('`')
                .context("a Markdown todo command has no closing backtick")?;

View on GitHub (pinned to e73179060b)