GitoxideLabs/gitoxide · warning

no illformed utf8

Error message

no illformed utf8

What it means

`NameRef::try_from` validates attribute names against an ASCII allowlist (`-._A-Za-z0-9`). Since the byte check guarantees ASCII, `to_str()` is expected to succeed; the `.expect("no illformed utf8")` panics only if the validation predicate and conversion fall out of sync.

Solutions

  1. Fix the attribute name in `.gitattributes` to use only `-`, `.`, `_`, letters and digits.
  2. Keep `attr_valid` and the `to_str()` conversion in sync — the predicate already implies ASCII/UTF-8 safety.
  3. Handle the returned `name::Error` instead of relying on the expect path.

Example fix

// .gitattributes before
*.rs mein-atträbute text

// after
*.rs my-attr text
Defensive patterns

Strategy: validation

Validate before calling

fn attr_name_valid(name: &[u8]) -> bool {
    !name.is_empty()
        && name.iter().all(|b| matches!(b, b'-' | b'.' | b'_' | b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9'))
}

Try / catch

match NameRef::try_from(attr) {
    Ok(name) => use(name),
    Err(e) => report_bad_attribute(&e.attribute),
}

Prevention

When it happens

Trigger: Parsing a `.gitattributes` file whose attribute name contains bytes outside the allowlist — this produces the `Error { attribute }` (no panic); the panic itself only occurs if `attr_valid` accepts non-UTF-8 bytes due to a predicate bug.

Common situations: Malformed `.gitattributes` entries with unusual characters (e.g. non-ASCII names) yield the regular `name::Error`; developers editing the validation predicate could introduce the panic path.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at gix-attributes/src/name.rs:40

        self.0
    }
}

impl<'a> TryFrom<&'a BStr> for NameRef<'a> {
    type Error = Error;

    fn try_from(attr: &'a BStr) -> Result<Self, Self::Error> {
        fn attr_valid(attr: &BStr) -> bool {
            if attr.is_empty() || attr.first() == Some(&b'-') {
                return false;
            }

            attr.bytes()
                .all(|b| matches!(b, b'-' | b'.' | b'_' | b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9'))
        }

        attr_valid(attr)
            .then(|| NameRef(attr.to_str().expect("no illformed utf8")))
            .ok_or_else(|| Error { attribute: attr.into() })
    }
}

impl<'a> Name {
    /// Provide our ref-type.
    pub fn as_ref(&'a self) -> NameRef<'a> {
        NameRef(self.as_str())
    }

    /// Return the inner `str`.
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {

View on GitHub (pinned to e73179060b)