GitoxideLabs/gitoxide · warning

valid ref

Error message

valid ref

What it means

This is a Rust `expect()` panic inside `FullNameRef::file_name()` in gix-ref. The method splits the full ref name on `/` with `rsplitn(2, ...)` and takes the last segment; the `expect("valid ref")` asserts that `rsplitn` always yields at least one piece. Since `rsplitn` on any (even empty) byte string always returns at least one item, this panic fires only if the underlying `BStr` invariant of a `FullNameRef` was broken (e.g. the struct was constructed from a null/empty name via `new_unchecked` on garbage memory or via unsafe/FFI paths).

Solutions

  1. Construct ref names only through validating APIs (`FullName::try_from`, `gix_ref::fullname::FullName::try_from(...)`) rather than `new_unchecked`.
  2. Verify the name bytes are non-empty and contain no leading/trailing `/` before wrapping them as a `FullName`.
  3. If you control input, sanitize it with the same rules git uses (see `gix_ref::validate::prelude` helpers) before conversion.
  4. If the panic occurs on names produced by the library itself, report it upstream with the exact ref name bytes; it is an internal invariant violation.

Example fix

// before
let name = unsafe { FullNameRef::new_unchecked(BStr::new(&raw_bytes)) };
let file = name.file_name(); // may panic
// after
let name = FullName::try_from(raw_bytes.as_bstr())?; // validates
let file = name.as_ref().file_name();
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_fullname(name: &gix_ref::FullNameRef) -> bool {
    !name.as_bstr().is_empty()
}
// use validating constructor before calling file_name()
let name = gix_ref::FullName::try_from(bytes.as_bstr())?;

Type guard

fn file_name_safe(name: &gix_ref::FullNameRef) -> Option<&bstr::BStr> {
    if name.as_bstr().is_empty() { None } else { Some(name.file_name()) }
}

Prevention

When it happens

Trigger: Calling `FullNameRef::file_name()` on a `FullNameRef` whose inner bytes were constructed outside the library's validation (e.g. `FullName::try_from` was bypassed with `new_unchecked` on an empty or non-UTF8-safe buffer, or a ref name was hand-crafted from raw bytes read from disk).

Common situations: Users building `gix_ref::FullName` from raw bytes of their own ref-storage format; FFI/unsafe code reinterpreting a `FullNameRef`; corrupted ref files fed through low-level APIs instead of the validating constructors.

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

Appendix: source

Thrown at gix-ref/src/fullname.rs:255

    }

    /// Classify this name, or return `None` if it's unclassified.
    pub fn category(&self) -> Option<crate::Category<'_>> {
        self.as_ref().category()
    }

    /// Classify this name, or return `None` if it's unclassified. If `Some`,
    /// the shortened name is returned as well.
    pub fn category_and_short_name(&self) -> Option<(crate::Category<'_>, &BStr)> {
        self.as_ref().category_and_short_name()
    }
}

impl FullNameRef {
    /// Return the file name portion of a full name, for instance `main` if the
    /// full name was `refs/heads/main`.
    pub fn file_name(&self) -> &BStr {
        self.0.rsplitn(2, |b| *b == b'/').next().expect("valid ref").as_bstr()
    }
}

impl Borrow<FullNameRef> for FullName {
    #[inline]
    fn borrow(&self) -> &FullNameRef {
        FullNameRef::new_unchecked(self.0.as_bstr())
    }
}

impl AsRef<FullNameRef> for FullName {
    fn as_ref(&self) -> &FullNameRef {
        self.borrow()
    }
}

impl ToOwned for FullNameRef {
    type Owned = FullName;

View on GitHub (pinned to e73179060b)