pydantic/monty · critical

StringId overflow

Error message

StringId overflow

What it means

While interning a string, `intern_str` computes a new `StringId` from `strings.len() + INTERN_STRING_ID_OFFSET` and converts it to the ID's backing integer with `expect`. If the count plus offset exceeds the ID capacity, this panics. It guards the interpreter's assumption that all strings in a compilation fit the ID space.

Source

Thrown at crates/monty/src/intern.rs:1447

    pub fn get_str(&self, id: StringId) -> &str {
        get_str(&self.strings, id)
    }
}

/// Interns `s` into a `string_map`/`strings` pair, shared by [`InternerBuilder`]
/// and [`Interns`] so both tables allocate ids identically.
///
/// Single-ASCII and [`StaticStrings`] values resolve to their reserved ids
/// without touching the pool; everything else is deduplicated via `string_map`.
fn intern_str(string_map: &mut AHashMap<String, StringId>, strings: &mut Vec<WithHash<String>>, s: &str) -> StringId {
    if s.len() == 1 {
        StringId::from_ascii(s.as_bytes()[0])
    } else if let Ok(ss) = StaticStrings::from_str(s) {
        ss.into()
    } else {
        *string_map.entry(s.to_owned()).or_insert_with(|| {
            let string_id = strings.len() + INTERN_STRING_ID_OFFSET;
            let id = StringId(string_id.try_into().expect("StringId overflow"));
            strings.push(WithHash::for_str(s.to_owned()));
            id
        })
    }
}

/// Reverse of [`get_str`]: the `StringId` for `s`, or `None` if never interned.
///
/// Single ASCII char and [`StaticStrings`] ids live in reserved slot ranges
/// below [`INTERN_STRING_ID_OFFSET`], never in `string_map` — the cheap
/// branches come first.
fn get_string_id_by_name(string_map: &AHashMap<String, StringId>, s: &str) -> Option<StringId> {
    if s.len() == 1 {
        Some(StringId::from_ascii(s.as_bytes()[0]))
    } else if let Ok(ss) = StaticStrings::from_str(s) {
        Some(ss.into())
    } else {
        string_map.get(s).copied()

View on GitHub (pinned to adc986b362)

Solutions

  1. Reuse or reset the InternTable per compilation so the string count stays bounded.
  2. Widen `StringId` (e.g. to u64) if the workload legitimately needs more IDs.
  3. Treat as a bug report if triggered by realistic input.

Example fix

// before
let id = StringId(string_id.try_into().expect("StringId overflow"));
// after
let id = StringId(u32::try_from(string_id).map_err(|_| CompileError::TooManyInternedStrings)?);
Defensive patterns

Strategy: validation

Validate before calling

assert!(intern_table.string_count() + INTERN_STRING_ID_OFFSET < u32::MAX as usize, "StringId space exhausted");

Prevention

When it happens

Trigger: Interning a string when the number of already-interned strings plus `INTERN_STRING_ID_OFFSET` exceeds the `StringId` backing integer's maximum (e.g. > u32::MAX strings in one table).

Common situations: Only reachable with billions of distinct string literals/identifiers in one intern table, or a leaked/reused-table bug causing unbounded growth.

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