{"record":{"id":"555953b201038e24","repo":"pydantic/monty","slug":"invalid-ascii-byte","errorCode":null,"errorMessage":"invalid ascii byte","messagePattern":"invalid ascii byte","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/monty/src/intern.rs","lineNumber":94,"sourceCode":"/// them in lockstep — both tables must agree on the same `&str` per byte.\npub(crate) static ASCII_STRS: [&str; 128] = const {\n    // Initialize array of 128 bytes which will be used as the raw storage\n    const ASCII_BYTES: [u8; 128] = const {\n        let mut bytes: [u8; 128] = [0; 128];\n        let mut i: u8 = 0;\n        while i < 128 {\n            bytes[i as usize] = i;\n            i += 1;\n        }\n        bytes\n    };\n    // Index into the above array to build the `&'static str` forms\n    let mut strs: [&str; 128] = [\"\"; 128];\n    let mut i = 0;\n    while i < 128 {\n        strs[i] = match str::from_utf8(from_ref(&ASCII_BYTES[i])) {\n            Ok(s) => s,\n            Err(_) => panic!(\"invalid ascii byte\"),\n        };\n        i += 1;\n    }\n    strs\n};\n\n/// Static string values which are known at compile time and don't need to be interned.\n///\n/// Discriminant starts from STATIC_STRING_ID_OFFSET to make conversion to/from stringid\n/// cheap when within bounds. Discriminants are serialized `StringId`s, so append new\n/// variants at the end — inserting one shifts every later id.\n#[repr(u16)]\n#[derive(\n    Debug,\n    Clone,\n    Copy,\n    FromRepr,\n    EnumCount,","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/intern.rs#L76-L112","documentation":"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.","triggerScenarios":"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.","commonSituations":"Refactoring intern.rs (changing array size, initializer, or encoding) breaks the assumption that all 128 entries are ASCII.","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."],"exampleFix":"// before\nstatic ASCII_BYTES: [u8; 128] = generate_bytes(); // possibly wrong\n// after\nconst ASCII_BYTES: [u8; 128] = {\n    let mut b = [0u8; 128];\n    let mut i = 0;\n    while i < 128 { b[i] = i as u8; i += 1; }\n    b\n};","handlingStrategy":"validation","validationCode":"// assert table shape before relying on the lazy table\nassert_eq!(ASCII_BYTES.len(), 128);\nassert!(ASCII_BYTES.iter().enumerate().all(|(i, b)| *b == i as u8));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","internal-invariant","string-interning"],"backgroundTag":"internal-invariant-violation","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}