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
- Reuse or reset the InternTable per compilation so the string count stays bounded.
- Widen `StringId` (e.g. to u64) if the workload legitimately needs more IDs.
- 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
- Keep per-compilation string volume bounded
- Reuse intern tables instead of growing one indefinitely
- Report occurrences with the offending source
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
- LongIntId overflow
- Invalid static string ID
- StringId overflow while building reverse interns map
- dict_keys view must reference a dict
- dict_items view must always reference a dict
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/09a49fef633170b0.
Report an issue: GitHub.