pydantic/monty · info
index bounded by table length
Error message
index bounded by table length
What it means
Internal `expect` in `translate`, the helper implementing `bytes.translate`-style byte mapping for the base64 module. The identity table is built by enumerating 0..255, so `u8::try_from(i)` cannot fail — the message states the index is bounded by the table length. Not reachable from any public base64/b32 API call.
Solutions
- No runtime mitigation; keep the table at `[u8; 256]` and the enumerate loop over its full length.
- If the signature changes to take arbitrary table sizes, replace the `expect` with real error handling.
Defensive patterns
Strategy: validation
Prevention
- Keep `translate`'s table at 256 entries; prefer deriving the identity table from `0..=u8::MAX` so sizes stay coupled.
When it happens
Trigger: Unreachable; would require the table array size to be reduced below 256 (or the loop range changed) in a code edit.
Common situations: Only relevant to contributors altering `translate` or adding new translation-based codec helpers.
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/0778498c0fd0900f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/monty/src/modules/base64.rs:1148
Self::Given(value) => value.py_contains(&Value::Int(i64::from(byte)), vm),
}
}
}
/// Builds the plain `ValueError` the ascii85 and base85 codecs raise — not
/// `binascii.Error`, since none of them reaches `binascii`.
fn codec_value_error(message: impl Into<String>) -> RunError {
SimpleException::new_msg(ExcType::ValueError, message.into()).into()
}
/// Rewrites `data` in place, mapping each byte of `from` to the byte at the
/// same index of `to` — CPython's `bytes.translate(bytes.maketrans(...))`.
///
/// Later entries win, matching `maketrans` when `from` repeats a byte.
fn translate(data: &mut [u8], from: &[u8], to: &[u8]) {
let mut table: [u8; 256] = [0; 256];
for (i, slot) in table.iter_mut().enumerate() {
*slot = u8::try_from(i).expect("index bounded by table length");
}
for (src, dst) in from.iter().zip(to) {
table[usize::from(*src)] = *dst;
}
for byte in data {
*byte = table[usize::from(*byte)];
}
}
/// Borrows the bytes of an encoder input.
///
/// Encoders reach `binascii` without a coercion step, so a `str` fails the
/// buffer protocol with CPython's `a bytes-like object is required` wording.
pub(super) fn encode_input<'a>(value: &'a Value, vm: &'a VM<'_>) -> RunResult<Cow<'a, [u8]>> {
encode_input_prefixed(value, vm, "")
}
/// Borrows the bytes of an encoder that coerces with `memoryview(s).tobytes()`View on GitHub (pinned to adc986b362)