pydantic/monty · info

index bounded by alphabet length

Error message

index bounded by alphabet length

What it means

This is an internal Rust `expect` inside `base85_table`, which builds a 256-entry reverse-lookup table for an Ascii85/base85 alphabet. The `expect` fires only if a value produced by enumerating the 85-element alphabet does not fit in a `u8`, which is impossible since 85 < 256. It is an unreachable-safety net, not an error users can provoke through the base64 module API.

Source

Thrown at crates/monty/src/modules/base64.rs:925

                })?,
                None => 84,
            };
            acc = acc * 85 + u64::from(value);
        }
        let word = u32::try_from(acc)
            .map_err(|_| codec_value_error(format!("{codec} overflow in hunk starting at byte {start}")))?;
        out.extend_from_slice(&word.to_be_bytes());
    }

    out.truncate(out.len() - padding);
    Ok(out)
}

/// Reverse lookup for a base85 alphabet: index by byte, `None` outside it.
fn base85_table(alphabet: &[u8; 85]) -> [Option<u8>; 256] {
    let mut table = [None; 256];
    for (value, byte) in alphabet.iter().enumerate() {
        table[usize::from(*byte)] = Some(u8::try_from(value).expect("index bounded by alphabet length"));
    }
    table
}

/// Encodes bytes as Ascii85, five digits per four-byte word.
///
/// An all-zero word folds to `z` and, with `foldspaces`, four spaces fold to
/// `y`. A short final group is zero-padded to a full word and the digits that
/// padding produced are dropped again unless `pad` is set.
fn a85_encode(data: &[u8], pad: bool, foldspaces: bool) -> Vec<u8> {
    let padding = (4 - data.len() % 4) % 4;
    let mut out: Vec<u8> = Vec::with_capacity(data.len().div_ceil(4) * 5);
    // Where the final word's digits start, so the padding trim below can
    // rewrite them the way CPython rewrites `chunks[-1]`.
    let mut last_start = 0;

    for chunk in data.chunks(4) {
        last_start = out.len();

View on GitHub (pinned to adc986b362)

Solutions

  1. Do nothing at runtime; this line cannot fire with the shipped 85-byte alphabets.
  2. If it fires after your edit, restore the alphabet to exactly 85 unique bytes or change the `Some(u8::try_from(value)...)` conversion to match the new table width.
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Only reachable if a developer edits `base85_table` or an alphabet constant so the alphabet exceeds 256 entries or the enumerate index exceeds 255 — i.e. a source-level change, not any runtime input.

Common situations: Contributors modifying the base85 alphabets (ascii85, RFC 1924, z85) or refactoring the table construction; end users never hit it.

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/b4adc5aa5745a7ab. Report an issue: GitHub.