GitoxideLabs/gitoxide · error · anyhow::Error

must be a direct reference

Error message

{name} must be a direct reference

What it means

Type check in `edit::undo::read_queue_ref`, which reads one of the undo queue's refs (tip or cursor) and expects it to point directly at an object ID. If the ref is symbolic (points at another ref instead of a commit), the undo queue's invariants are broken — its positions are tracked as concrete object IDs — so the read bails naming the offending ref. Fired when queue refs were replaced or corrupted, e.g. by hand-editing or external git commands repointing them. Fix by resetting the queue refs to direct object targets or clearing the queue.

Solutions

  1. Check what the reference currently points at with git for-each-ref or tix, and if it is symbolic, clear the symbolic target so it stores an object ID directly
  2. Recreate the queue reference as a direct reference to the intended commit object
  3. Avoid tools or manual edits that attach symbolic targets to the internal undo-queue references
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at gix-tix/src/edit/undo.rs:821 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

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

    Ok(Some(Queue {
        tip,
        cursor,
        sentinel,
        entries,
        cursor_index,
    }))
}

fn read_queue_ref(repo: &gix::Repository, name: &str) -> Result<Option<ObjectId>> {
    let Some(reference) = repo
        .try_find_reference(name)
        .with_context(|| format!("could not read {name}"))?
    else {
        return Ok(None);
    };
    match reference.target() {
        gix::refs::TargetRef::Object(id) => Ok(Some(id.to_owned())),
        gix::refs::TargetRef::Symbolic(_) => bail!("{name} must be a direct reference"),
    }
}

struct ParsedCommit {
    parent: Option<ObjectId>,
    title: String,
    changes: Vec<RefChange>,
}

fn parse_commit(repo: &gix::Repository, id: ObjectId) -> Result<ParsedCommit> {
    let commit = repo
        .find_commit(id)
        .with_context(|| format!("could not find undo queue commit {id}"))?
        .decode()
        .context("could not decode an undo queue commit")?
        .into_owned()
        .context("could not own an undo queue commit")?;
    ensure!(

View on GitHub (pinned to e73179060b)