GitoxideLabs/gitoxide · error

Illformed UTF-8

Error message

Illformed UTF-8

What it means

`TryFrom<OsString> for Boolean` first converts the OS string into a `&BStr` via `gix_path::os_str_into_bstr`; when that conversion fails (the OS string is not representable as the byte string form, e.g. non-UTF-8 path-like data on some platforms) the code raises this Error labeled 'Illformed UTF-8' with the display form of the original value. It is a pre-parse failure, before boolean syntax is even examined.

Solutions

  1. Sanitize or reject non-UTF-8 input before calling `Boolean::try_from(OsString)`
  2. Convert the input to a `&BStr` yourself from known-good bytes (`BStr::new(&bytes)`) and use the `TryFrom<&BStr>` path
  3. Catch the Error and report which value was ill-formed to the user

Example fix

// before
let b = Boolean::try_from(os_value)?;
// after
let b = match std::str::from_utf8(os_value.as_bytes()) {
    Ok(s) => Boolean::try_from(BStr::new(s))?,
    Err(_) => return Err(/* report ill-formed UTF-8 input */),
};
Defensive patterns

Strategy: type-guard

Validate before calling

std::str::from_utf8(os_value.as_bytes()).map_err(|_| /* ill-formed UTF-8 */)?;

Type guard

fn is_utf8(v: &OsStr) -> bool { std::str::from_utf8(v.as_bytes()).is_ok() }

Try / catch

match Boolean::try_from(os_value) { Ok(b) => b, Err(e) => { log::warn!("invalid boolean input: {e}"); Boolean::default() } }

Prevention

When it happens

Trigger: `Boolean::try_from(os_string)` where `os_string` comes from an environment variable, command-line argument, or file path that contains bytes that cannot be converted (e.g. non-UTF-8 Windows/OsStr data).

Common situations: Config values sourced from raw OS strings on platforms where `OsStr` is not UTF-8 internally (Windows WTF-16), or shell arguments containing invalid UTF-8 bytes.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at gix-config-value/src/boolean.rs:19

use std::{borrow::Cow, ffi::OsString, fmt::Display};

use bstr::{BStr, BString, ByteSlice};

use crate::{Boolean, Error};

fn bool_err(input: impl Into<BString>) -> Error {
    Error::new(
        "Booleans need to be 'no', 'off', 'false', '' or 'yes', 'on', 'true' or any number",
        input,
    )
}

impl TryFrom<OsString> for Boolean {
    type Error = Error;

    fn try_from(value: OsString) -> Result<Self, Self::Error> {
        let value = gix_path::os_str_into_bstr(&value)
            .map_err(|_| Error::new("Illformed UTF-8", std::path::Path::new(&value).display().to_string()))?;
        Self::try_from(value)
    }
}

/// # Warning
///
/// The direct usage of `try_from("string")` is discouraged as it will produce the wrong result for values
/// obtained from `core.bool-implicit-true`, which have no separator and are implicitly true.
/// This method chooses to work correctly for `core.bool-empty=`, which is an empty string and resolves
/// to being `false`.
///
/// Instead of this, obtain booleans with `config.boolean(…)`, which handles the case were no separator is
/// present correctly.
impl TryFrom<&BStr> for Boolean {
    type Error = Error;

    fn try_from(value: &BStr) -> Result<Self, Self::Error> {
        if parse_true(value) {

View on GitHub (pinned to e73179060b)