pydantic/monty · info
masked below 2**32
Error message
masked below 2**32
What it means
Internal `expect` in `wrapping_u32` for arbitrary-precision (`LongInt`) seeds: the BigInt is bitwise-ANDed with `0xffffffff`, so the result fits a `u32` and `to_u32()` cannot return `None`. The two's-complement `&` reproduces Python's `value & 0xffffffff` for negatives. Not user-triggerable from `crc32`/`crc_hqx`.
Solutions
- No runtime action; preserve the `& 0xffffffff` masking before `to_u32()`.
Defensive patterns
Strategy: validation
Prevention
- Mask BigInt seeds with `& u32::MAX` before `to_u32()`; add a test seeding crc32 with a negative and a >2**32 integer.
When it happens
Trigger: Unreachable; requires the mask (`& BigInt::from(u32::MAX)`) to be removed or changed in a code edit.
Common situations: Only during refactors of the bigint seed path in `wrapping_u32`.
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
- Cannot get identity of Dereferenced object
- Dereferenced found while converting to MontyObject
- dict_keys view must always reference a dict
- dict view must always reference a dict
- entry just allocated
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/2e2415e2b0ee7397.
Report an issue: GitHub.
Appendix: source
Thrown at crates/monty/src/modules/binascii.rs:412
data: Value,
#[from_args(default = Value::Bool(false))]
header: Value,
}
/// Reduces an `int` to the `unsigned int` CPython's `I` format takes, which
/// wraps modulo 2**32 rather than rejecting a value out of range — so a
/// negative or huge `crc` seed is accepted, as CPython accepts it.
fn wrapping_u32(value: &Value, vm: &VM<'_>) -> RunResult<u32> {
const MODULUS: i64 = 1 << 32;
match value {
Value::Int(int) => Ok(u32::try_from(int.rem_euclid(MODULUS)).expect("reduced below 2**32")),
Value::Bool(flag) => Ok(u32::from(*flag)),
Value::Ref(heap_id) => match vm.heap.get(*heap_id) {
HeapData::LongInt(long) => {
// `&` on a `BigInt` is two's-complement, so this is Python's
// own `value & 0xffffffff` for negatives as well.
Ok((&long.0 & BigInt::from(u32::MAX)).to_u32().expect("masked below 2**32"))
}
_ => Err(ExcType::type_error_not_integer(&value.py_type_name(vm))),
},
_ => Err(ExcType::type_error_not_integer(&value.py_type_name(vm))),
}
}
/// Encodes bytes as lowercase hex, inserting `sep` every `bytes_per_sep` bytes.
///
/// A positive `bytes_per_sep` groups from the right, so any short group leads;
/// a negative one groups from the left. Zero, or no separator, means none.
fn hex_encode(data: &[u8], sep: Option<u8>, bytes_per_sep: i32) -> Vec<u8> {
let group = match sep {
Some(_) if bytes_per_sep != 0 && !data.is_empty() => usize::try_from(bytes_per_sep.unsigned_abs())
.expect("u32 fits usize on supported targets")
.min(data.len()),
// Without a separator, or with an empty input, emit one flat run.
_ => data.len().max(1),View on GitHub (pinned to adc986b362)