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

  1. No runtime mitigation; keep the table at `[u8; 256]` and the enumerate loop over its full length.
  2. If the signature changes to take arbitrary table sizes, replace the `expect` with real error handling.
Defensive patterns

Strategy: validation

Prevention

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


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)