GitoxideLabs/gitoxide · info

valid ASCII

Error message

valid ASCII

What it means

When printing index entry status, a change is mapped to a single status character which is converted to `&str` via `str::from_utf8` with an `.expect("valid ASCII")`. Since `change_to_char` only yields ASCII characters, this panics only if that invariant is broken by a future code change.

Solutions

  1. Verify `change_to_char` only returns ASCII status characters ('A', 'M', 'D', etc.).
  2. Refactor to return `char` or a static `&'static str` from `change_to_char` so the UTF-8 conversion cannot fail.
  3. If hit, inspect the byte value returned and fix the mapping table.

Example fix

// before
let char_storage = change_to_char(&change);
std::str::from_utf8(std::slice::from_ref(&char_storage)).expect("valid ASCII")

// after
fn change_to_char(c: &Change) -> &'static str { /* ... */ }
change_to_char(&change)
Defensive patterns

Strategy: type-guard

Type guard

fn is_ascii(b: &u8) -> bool { *b < 0x80 }
// assert change_to_char output is ASCII before conversion

Try / catch

// unreachable by design; refactor instead
change_to_char(&change) // return &'static str directly

Prevention

When it happens

Trigger: Calling `print_index_entry_status` (from `show`) where `change_to_char` returns a non-ASCII byte — currently unreachable; a regression in `change_to_char` would trigger it.

Common situations: None in practice for library users; only developers modifying the status-character mapping in gitoxide-core could hit this.

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

Appendix: source

Thrown at gitoxide-core/src/repository/status.rs:229

    progress.init(Some(out.worktree_index.entries().len()), gix::progress::count("files"));
    progress.set(out.worktree_index.entries().len());
    progress.show_throughput(start);
    Ok(())
}

fn print_index_entry_status(
    out: &mut dyn std::io::Write,
    prefix: &Path,
    rela_path: &BStr,
    status: EntryStatus<(), gix::submodule::Status>,
) -> std::io::Result<()> {
    let char_storage;
    let status = match status {
        EntryStatus::Conflict { summary, entries: _ } => as_str(summary),
        EntryStatus::Change(change) => {
            char_storage = change_to_char(&change);
            std::str::from_utf8(std::slice::from_ref(&char_storage)).expect("valid ASCII")
        }
        EntryStatus::NeedsUpdate(_stat) => {
            return Ok(());
        }
        EntryStatus::IntentToAdd => "A",
    };

    let rela_path = gix::path::from_bstr(rela_path);
    let display_path = gix::path::relativize_with_prefix(&rela_path, prefix);
    writeln!(out, "{status: >3} {}", display_path.display())
}

fn as_str(c: Conflict) -> &'static str {
    match c {
        Conflict::BothDeleted => "DD",
        Conflict::AddedByUs => "AU",
        Conflict::DeletedByThem => "UD",
        Conflict::AddedByThem => "UA",

View on GitHub (pinned to e73179060b)