GitoxideLabs/gitoxide · error

we are called from a valid ref

Error message

we are called from a valid ref

What it means

An `unreachable!()` panic in `must_be_io_err`, an internal gix-ref helper that downcasts a loose reflog error to `std::io::Error`. The helper is only ever invoked from code paths operating on an already-validated ref name, so a `RefnameValidation` error variant is considered impossible; the panic fires if validation-style errors leak into those call sites.

Solutions

  1. Verify the reference name is valid: `git check-ref-format --branch <name>` or `repo.find_reference(name)` before touching its reflog
  2. Clean up malformed refs/reflog files under `.git/refs` and `.git/logs` (or recreate them with real git)
  3. Upgrade gix / gix-ref; if a public API lets an invalid name reach the reflog reader, report it upstream
Defensive patterns

Strategy: validation

Validate before calling

fn ref_name_is_valid(name: &str) -> bool {
    // mirror git check-ref-format basics
    !name.is_empty()
        && !name.starts_with('-')
        && !name.contains("..")
        && !name.ends_with(".lock")
        && !name.chars().any(|c| matches!(c, ' ' | '~' | '^' | ':' | '?' | '*' | '[' | '\\'))
        && name.split('/').all(|c| !c.is_empty() && c != "." && !c.ends_with(".lock"))
}

Type guard

fn as_io_error(err: &gix_ref::store::file::loose::reflog::Error) -> Option<&std::io::Error> {
    if let gix_ref::store::file::loose::reflog::Error::Io(e) = err { Some(e) } else { None }
}

Try / catch

match std::panic::catch_unwind(|| read_reflog(ref)) {
    Ok(Ok(data)) => data,
    _ => Vec::new(), // treat unreadable/invalid reflog as absent
}

Prevention

When it happens

Trigger: Reading or iterating the reflog of a reference (e.g. `Reference::reflog_iter` internals) where the underlying loose-reflog reader returns `loose::reflog::Error::RefnameValidation` — i.e. the ref name was actually invalid despite the caller assuming validity, typically due to a validation bypass elsewhere or corrupted refs storage.

Common situations: Repositories with hand-created or oddly-encoded ref files under `.git/refs/` or `.git/logs/` (case/encoding quirks, Windows-reserved names), or gix versions where validation and iteration disagree about name validity.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at gix-ref/src/store/file/loose/reference/logiter.rs:9

use crate::store_impl::{
    file,
    file::{log, loose, loose::Reference},
};

pub(crate) fn must_be_io_err(err: loose::reflog::Error) -> std::io::Error {
    match err {
        loose::reflog::Error::Io(err) => err,
        loose::reflog::Error::RefnameValidation(_) => unreachable!("we are called from a valid ref"),
    }
}

impl Reference {
    /// Returns true if a reflog exists in the given `store`.
    ///
    /// Please note that this method shouldn't be used to check if a log exists before trying to read it, but instead
    /// is meant to be the fastest possible way to determine if a log exists or not.
    /// If the caller needs to know if it's readable, try to read the log instead with a reverse or forward iterator.
    pub fn log_exists(&self, store: &file::Store) -> bool {
        store
            .reflog_exists(self.name.as_ref())
            .expect("name conversion infallible")
    }
    /// Return a reflog reverse iterator for this ref, reading chunks from the back into the fixed buffer `buf`, in the given `store`.
    ///
    /// The iterator will traverse log entries from most recent to oldest, reading the underlying file in chunks from the back.
    /// Return `Ok(None)` if no reflog exists.

View on GitHub (pinned to e73179060b)