{"record":{"id":"81feafa80637637d","repo":"pydantic/monty","slug":"free-var-name-str-not-found-in-enclosing-scope-s-cells","errorCode":null,"errorMessage":"free_var '{name_str}' not found in enclosing scope's cells, comprehension targets, or globals","messagePattern":"free_var '(.+?)' not found in enclosing scope's cells, comprehension targets, or globals","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/monty/src/prepare.rs","lineNumber":397,"sourceCode":"        for scope in self.comp_name_scopes.iter_mut().rev() {\n            if let Some(binding) = scope.get_mut(&name_id) {\n                binding.captured = true;\n                return CaptureSource::CompVar(binding.slot);\n            }\n        }\n        if let PrepareState::Function(state) = &self.state {\n            if let Some(&slot) = state.cell_var_map.get(&name_id) {\n                return CaptureSource::Namespace(slot);\n            }\n            if let Some(&slot) = state.free_var_map.get(&name_id) {\n                return CaptureSource::Namespace(slot);\n            }\n        }\n        if let Some(slot) = self.globals.globals.get(name_id) {\n            return CaptureSource::Namespace(slot);\n        }\n        let name_str = self.interner.get_str(name_id);\n        panic!(\"free_var '{name_str}' not found in enclosing scope's cells, comprehension targets, or globals\");\n    }\n\n    /// Inner-to-outer scope hand-off when a just-prepared child scope reports a\n    /// capture that wasn't predicted by scope analysis.\n    ///\n    /// The recursive [`collect_referenced_names_from_node`] pass below\n    /// pre-populates most transitively captured names before the body is\n    /// walked, but its `ClassDef` arm collects only decorators, nothing from the\n    /// class body — so for a capture chain that flows through a class body (a\n    /// method capturing an enclosing function's local), this bubble-up is\n    /// **load-bearing**, not a safety net: it is the only mechanism that\n    /// registers the intermediate scopes' cells. It classifies each late\n    /// discovery:\n    ///\n    /// - Already a cell or free var here → nothing to do.\n    /// - Bound locally (params or body-assigned) → register as a cell var here.\n    /// - Bound in an ancestor scope (`enclosing_locals`) → register as a\n    ///   pass-through free var here so the cell propagates upward.","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/prepare.rs#L379-L415","documentation":"During bytecode preparation, a name captured as a free variable by a child scope must be resolvable in the enclosing scope as a cell, a comprehension target, or a global. If the interner lookup fails in all three places, preparation panics — this indicates a scope-analysis bug where a capture was predicted but the binding site was never recorded.","triggerScenarios":"Compiling Python code where a nested function/comprehension references a free variable that scope analysis flagged as captured but that is not bound anywhere in an enclosing scope, e.g. an inner function reading a name only bound in a sibling scope or deleted before preparation.","commonSituations":"Contributors modifying the scope-analysis pass (collect_referenced_names_from_node / build_free_var_slots); test code with unusual closure patterns exercising capture prediction edge cases.","solutions":["Fix the scope-analysis pass so names bound nowhere are reported as compile errors rather than predicted captures.","Verify the enclosing-scope cell map and comprehension target lists are populated before build_free_var_slots runs.","Reduce the failing Python snippet and check whether CPython raises a SyntaxError/NameError for it — match that behavior instead of panicking."],"exampleFix":"// before\n// scope analysis marks 'x' captured but never records its binding\npanic!(\"free_var '{name_str}' not found...\");\n// after\n// emit a proper compile error instead\nreturn Err(PrepareError::unbound_free_variable(name_id, position));","handlingStrategy":"validation","validationCode":"// pre-check capture resolution in scope analysis (Rust, before finalize)\nif !state.cell_var_map.contains_key(&name_id)\n    && !state.comprehension_targets.contains(&name_id)\n    && !globals.contains_key(&name_id) {\n    return Err(PrepareError::unbound_free_variable(name_id));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compare capture behavior against CPython for tricky closures","Add compile-error tests for names bound nowhere instead of panics","Keep capture prediction and binding registration in one pass"],"tags":["rust","compiler","scope-analysis"],"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-14T11:17:12.474Z"}