{"record":{"id":"764b75c312d96d82","repo":"pydantic/monty","slug":"stringid-overflow-while-building-reverse-interns-map","errorCode":null,"errorMessage":"StringId overflow while building reverse interns map","messagePattern":"StringId overflow while building reverse interns map","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/monty/src/intern.rs","lineNumber":1578,"sourceCode":"        }\n    }\n}\n\n/// Builds the `String → StringId` reverse map from the `strings` vector.\n///\n/// Used both at fresh [`Interns::new`] time and after deserialization. The\n/// ids start at [`INTERN_STRING_ID_OFFSET`] because slots `< OFFSET` are\n/// reserved for ASCII single-character strings and the [`StaticStrings`]\n/// table — those are handled by the cheap branches at the top of\n/// [`Interns::get_string_id_by_name`] and never enter this map.\nfn build_string_id_by_name(strings: &[WithHash<String>]) -> AHashMap<String, StringId> {\n    strings\n        .iter()\n        .enumerate()\n        .map(|(index, entry)| {\n            let id = StringId(\n                u32::try_from(INTERN_STRING_ID_OFFSET + index)\n                    .expect(\"StringId overflow while building reverse interns map\"),\n            );\n            (entry.value().clone(), id)\n        })\n        .collect()\n}\n\nimpl Interns {\n    /// Builds the runtime table from a finished parse/prepare interner and the\n    /// functions compiled against it.\n    pub fn new(interner: InternerBuilder, functions: Vec<Function>) -> Self {\n        // `InternerBuilder` already maintains the `String → StringId` map\n        // during the parse/prepare phase to deduplicate `intern` calls;\n        // we move it across so `Interns::get_string_id_by_name` doesn't\n        // have to rebuild the same table from `strings`.\n        Self {\n            strings: interner.strings,\n            bytes: interner.bytes,\n            long_ints: interner.long_ints,","sourceCodeStart":1560,"sourceCodeEnd":1596,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/intern.rs#L1560-L1596","documentation":"`build_string_id_by_name` rebuilds the name→`StringId` reverse map and re-derives each id from the index plus `INTERN_STRING_ID_OFFSET`; the `expect` panics if that sum exceeds the ID's backing integer. Same invariant as the intern-time `StringId overflow` check, evaluated when the lookup table is constructed.","triggerScenarios":"Calling `InternTable::from` (which builds the reverse map) on a table containing more strings than fit in `StringId` after the offset.","commonSituations":"Billions of interned strings in one table, or a table-reuse bug allowing unbounded growth before the reverse map is built.","solutions":["Bound the intern table size per compilation/execution and reuse tables.","Widen `StringId` if the workload needs a larger ID space.","Report as a bug if reached with realistic input."],"exampleFix":"// before\n.expect(\"StringId overflow while building reverse interns map\")\n// after\nu32::try_from(INTERN_STRING_ID_OFFSET + index)\n    .map_err(|_| CompileError::TooManyInternedStrings)?","handlingStrategy":"validation","validationCode":"assert!(strings.len() + INTERN_STRING_ID_OFFSET < u32::MAX as usize, \"StringId space exhausted\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Bound table size before building the reverse map","Reuse tables per compilation","Treat as a bug report"],"tags":["rust","panic","interning"],"backgroundTag":"internal-invariant-violation","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}