{"record":{"id":"ffb0e628c8b52640","repo":"pydantic/monty","slug":"bubble-up-captured-name-str-that-is-bound-nowhere-scope","errorCode":null,"errorMessage":"bubble-up captured '{name_str}' that is bound nowhere — scope analysis bug","messagePattern":"bubble-up captured '(.+?)' that is bound nowhere — scope analysis bug","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/monty/src/prepare.rs","lineNumber":442,"sourceCode":"        }\n\n        let PrepareState::Function(state) = &mut self.state else {\n            return Ok(());\n        };\n\n        if state.cell_var_map.contains_key(&captured_name) || state.free_var_map.contains_key(&captured_name) {\n            return Ok(());\n        }\n\n        if state.assigned_names.contains(&captured_name) || state.locals.contains(captured_name) {\n            let slot = state.locals.ensure_slot(captured_name, position)?;\n            state.cell_var_map.insert(captured_name, slot);\n        } else if state.enclosing_locals.contains(&captured_name) {\n            let slot = state.locals.ensure_slot(captured_name, position)?;\n            state.free_var_map.insert(captured_name, slot);\n        } else {\n            let name_str = self.interner.get_str(captured_name);\n            panic!(\"bubble-up captured '{name_str}' that is bound nowhere — scope analysis bug\");\n        }\n        Ok(())\n    }\n\n    /// Builds the parallel free-var slot vectors for a just-prepared child\n    /// scope from its `free_var_map` (`name -> the child's own slot`).\n    ///\n    /// Returns `(free_var_slots, free_var_enclosing_slots)`: the first holds the\n    /// child's own slots (where it installs each captured cell at call time);\n    /// the second holds OUR slot it reads that cell from when the closure is\n    /// built (via [`Self::lookup_captured_slot`]). Both are ordered by the\n    /// child slot so they stay index-aligned.\n    fn build_free_var_slots(\n        &mut self,\n        inner_free_var_map: AHashMap<StringId, NamespaceId>,\n    ) -> (Vec<NamespaceId>, Vec<CaptureSource>) {\n        let mut entries: Vec<_> = inner_free_var_map.into_iter().collect();\n        entries.sort_by_key(|(_, inner_slot)| *inner_slot);","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/prepare.rs#L424-L460","documentation":"When a child scope's capture is bubbled up to its parent during scope finalization, the captured name must exist in the parent's cell vars or enclosing locals. If it is bound in neither, preparation panics with a message explicitly flagging a scope-analysis bug — the capture should never have been predicted.","triggerScenarios":"Finalizing a nested scope whose captured name the parent never bound: e.g. the analysis recorded a capture from a pass that ran before the parent's binding information was complete, or a name is bound only in a scope not on the capture chain.","commonSituations":"Contributors editing finalize_child_scope or the capture-prediction logic; test cases with deeply nested closures and class scopes where capture chains are hard to predict.","solutions":["Fix capture prediction so only names actually bound in the chain are marked captured.","Check that cell_var_map and enclosing_locals are merged from ancestor scopes before finalization.","Reproduce with a minimal Python snippet and compare against CPython's closure behavior to find the missing binding site."],"exampleFix":"// before\npanic!(\"bubble-up captured '{name_str}' that is bound nowhere — scope analysis bug\");\n// after\n// skip the spurious capture and let name resolution fall back to globals\nreturn Ok(()); // once prediction is fixed to not emit it","handlingStrategy":"validation","validationCode":"// guard before bubbling up a capture\nassert!(state.cell_var_map.contains_key(&captured_name) || state.enclosing_locals.contains(&captured_name));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Merge ancestor cell/local maps before finalizing child scopes","Write nested-closure test cases spanning function, class and comprehension scopes","Only mark captures for names verified bound in the chain"],"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-14T16:17:12.679Z"}