GitoxideLabs/gitoxide · error

the undo queue cannot record itself

Error message

the undo queue cannot record itself

What it means

`normalize_changes` validates a batch of reference changes before they are recorded on the undo queue. Any change whose target name is one of the undo queue's own refs (tip, cursor, etc., matched by `is_queue_ref`) is rejected, because recording a change to the queue refs on the queue itself would create self-referential, un-undoable state.

Solutions

  1. Filter out queue refs from your change list before recording (use the same prefix test the library uses).
  2. Never write directly to the undo queue's tip/cursor refs; use the library's public undo/redo APIs.
  3. If you need to reset the queue, use its reset/discard API instead of crafting a RefChange.

Example fix

// before: recording every ref change
let changes: Vec<RefChange> = all_ref_updates();
repo.undo_record(changes)?;
// after: drop queue-internal refs first
let changes: Vec<RefChange> = all_ref_updates()
    .into_iter()
    .filter(|c| !crate::history::is_queue_ref(c.name.as_bstr()))
    .collect();
Defensive patterns

Strategy: validation

Validate before calling

// filter queue-internal refs out of any change batch before recording
let changes: Vec<RefChange> = changes
    .into_iter()
    .filter(|c| !crate::history::is_queue_ref(c.name.as_bstr()))
    .collect();

Prevention

When it happens

Trigger: Calling `record`, `changes_from_edits`, or `apply_reversed_changes` with a `RefChange` whose `name` points at an undo-queue ref (e.g. the queue tip/cursor ref).

Common situations: Custom automation that enumerates all refs and builds ref updates blindly, capturing the queue's internal refs; misconfigured tools that treat queue refs as ordinary refs.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at gix-tix/src/edit/undo.rs:341

        title,
        changes,
        edits,
        position: queue.position(cursor_index),
    })
}

fn empty_position() -> Position {
    Position {
        title: START_TITLE.into(),
        undo: 0,
        redo: 0,
    }
}

fn normalize_changes(changes: impl IntoIterator<Item = RefChange>) -> Result<Vec<RefChange>> {
    let mut by_name = BTreeMap::<FullName, RefChange>::new();
    for change in changes {
        ensure!(
            !is_queue_ref(change.name.as_bstr()),
            "the undo queue cannot record itself"
        );
        match by_name.get_mut(&change.name) {
            Some(existing) => {
                ensure!(
                    existing.after == change.before,
                    "successive changes to {} are not continuous",
                    change.name
                );
                existing.after = change.after;
            }
            None => {
                by_name.insert(change.name.clone(), change);
            }
        }
    }
    Ok(by_name

View on GitHub (pinned to e73179060b)