GitoxideLabs/gitoxide · error

the undo queue is not a selectable revision

Error message

the undo queue is not a selectable revision

What it means

When computing the refs that a set of user-supplied revisions points at, tix forbids selecting the undo queue (or any ref whose chain reaches the undo queue) as an explicit revision, because the queue is internal bookkeeping, not user-selectable history. For each reference resolved from the rev-spec, `ref_chain_reaches_queue` is consulted; if the ref's chain reaches the queue, this error is raised.

Solutions

  1. Do not reference the undo queue (or refs resolving through it) in revision arguments; use the real branch/tag names instead
  2. Check whether a branch was accidentally repointed at the queue and reset it to the intended commit
  3. If a script auto-selects revisions, filter out refs matching the undo-queue prefix before passing them
  4. Run `tix undo`/recovery to restore refs whose chains reach the queue

Example fix

// before
tix history refs/tix/undo/queue main
// after
tix history main
Defensive patterns

Strategy: validation

Validate before calling

// reject queue-reaching refs before passing revisions to tix
fn reaches_queue(name: &str) -> bool {
    name.starts_with("refs/tix/undo/")
}
for rev in revisions {
    if let Some(name) = rev.strip_prefix("refs/").map(|_| rev) {
        assert!(!reaches_queue(name), "{} selects the undo queue", rev);
    }
}

Type guard

fn is_queue_revision(rev: &std::ffi::OsStr) -> bool {
    let s = rev.to_string_lossy();
    s.contains("refs/tix/undo/")
}

Try / catch

match referenced_refs(&repo, &revisions) {
    Ok(map) => map,
    Err(e) if e.to_string().contains("not a selectable revision") => {
        eprintln!("revision resolves into the undo queue; use a real branch or tag");
        std::process::exit(2);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `referenced_refs` (e.g. from commands that take revision arguments) with a revision spec such as `refs/tix/undo/queue`, a branch that points into or resolves through the queue, or a range like `queue..main` whose first/second reference chain reaches the queue.

Common situations: A user passes an undo-queue ref or a branch accidentally repointed at the queue directly on the command line; scripts capture queue ref names from `git for-each-ref` output and feed them back as revisions; a corrupted undo operation leaves a user branch pointing at queue commits.

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

Appendix: source

Thrown at gix-tix/src/history.rs:1582

) -> Result<HashMap<BString, gix::refs::Target>> {
    if revisions.is_empty() && repo.head()?.is_unborn() {
        return Ok(HashMap::new());
    }
    let implicit_head = OsString::from("HEAD");
    let revisions = if revisions.is_empty() {
        std::slice::from_ref(&implicit_head)
    } else {
        revisions
    };
    let mut out = HashMap::new();
    for revision in revisions {
        let revision = gix::path::os_str_into_bstr(revision)
            .with_context(|| format!("revision {} is not valid UTF-8", revision.to_string_lossy()))?;
        let spec = repo
            .rev_parse(revision)
            .with_context(|| format!("could not parse revision {revision}"))?;
        for reference in [spec.first_reference(), spec.second_reference()].into_iter().flatten() {
            anyhow::ensure!(
                !crate::edit::undo::ref_chain_reaches_queue(repo, reference.name.as_ref())?,
                "the undo queue is not a selectable revision"
            );
            insert_ref_chain(repo, reference.name.as_bstr(), &mut out)?;
        }
    }
    Ok(out)
}

fn insert_ref_chain(repo: &gix::Repository, name: &BStr, out: &mut HashMap<BString, gix::refs::Target>) -> Result<()> {
    let mut name = name.to_owned();
    loop {
        if out.contains_key(&name) {
            return Ok(());
        }
        let reference = match repo.try_find_reference(name.as_bstr()) {
            Ok(reference) => reference,
            Err(err) if is_missing_ref(&err) => return Ok(()),

View on GitHub (pinned to e73179060b)