pydantic/monty · error
invalid ascii byte
Error message
invalid ascii byte
What it means
A const-evaluated table of all 128 ASCII byte values is built by decoding each byte as UTF-8 at first use. Since bytes 0x00–0x7F are always valid ASCII/UTF-8, the decode can never fail; the panic is a defensive invariant against the ASCII_BYTES table being corrupted or reordered.
Source
Thrown at crates/monty/src/intern.rs:94
/// them in lockstep — both tables must agree on the same `&str` per byte.
pub(crate) static ASCII_STRS: [&str; 128] = const {
// Initialize array of 128 bytes which will be used as the raw storage
const ASCII_BYTES: [u8; 128] = const {
let mut bytes: [u8; 128] = [0; 128];
let mut i: u8 = 0;
while i < 128 {
bytes[i as usize] = i;
i += 1;
}
bytes
};
// Index into the above array to build the `&'static str` forms
let mut strs: [&str; 128] = [""; 128];
let mut i = 0;
while i < 128 {
strs[i] = match str::from_utf8(from_ref(&ASCII_BYTES[i])) {
Ok(s) => s,
Err(_) => panic!("invalid ascii byte"),
};
i += 1;
}
strs
};
/// Static string values which are known at compile time and don't need to be interned.
///
/// Discriminant starts from STATIC_STRING_ID_OFFSET to make conversion to/from stringid
/// cheap when within bounds. Discriminants are serialized `StringId`s, so append new
/// variants at the end — inserting one shifts every later id.
#[repr(u16)]
#[derive(
Debug,
Clone,
Copy,
FromRepr,
EnumCount,View on GitHub (pinned to adc986b362)
Solutions
- Inspect the ASCII_BYTES constant and restore it so entries 0..128 equal their own byte value.
- Verify the array length is exactly 128 and `from_ref` points at raw bytes, not a transformed representation.
- Replace the table with a safer generation (e.g. `char::from_u32` loop) so the invariant is enforced at compile time.
Example fix
// before
static ASCII_BYTES: [u8; 128] = generate_bytes(); // possibly wrong
// after
const ASCII_BYTES: [u8; 128] = {
let mut b = [0u8; 128];
let mut i = 0;
while i < 128 { b[i] = i as u8; i += 1; }
b
}; Defensive patterns
Strategy: validation
Validate before calling
// assert table shape before relying on the lazy table assert_eq!(ASCII_BYTES.len(), 128); assert!(ASCII_BYTES.iter().enumerate().all(|(i, b)| *b == i as u8));
Prevention
- Keep ASCII_BYTES a const generated in a while-loop from its index
- Add a unit test asserting ASCII_BYTES[i] == i for all 128 entries
- Avoid hand-editing generated byte tables
When it happens
Trigger: A contributor modifies the ASCII_BYTES static so that an entry is no longer a valid single ASCII byte, then any string interning triggers lazy table construction.
Common situations: Refactoring intern.rs (changing array size, initializer, or encoding) breaks the assumption that all 128 entries are ASCII.
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
- gather commit frame id is not a GatherFuture
- gather item is not a Coroutine, ExternalFuture, or GatherFut
- Cannot get identity of Dereferenced object
- Undefined found while converting to MontyObject
- Dereferenced found while converting to MontyObject
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/555953b201038e24.
Report an issue: GitHub.