{"record":{"id":"3261d54ae71212b3","repo":"astral-sh/ruff","slug":"checked-bindings-are-stable-across-fixpoint-iterat","errorCode":null,"errorMessage":"checked bindings are stable across fixpoint iterations","messagePattern":"checked bindings are stable across fixpoint iterations","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/call/bind.rs","lineNumber":999,"sourceCode":"\n        let mut functions = SmallVec::new();\n        collect(db, self, &mut functions);\n        functions.into_iter()\n    }\n\n    /// Returns an iterator over all `CallableBinding`s, flattening the two-level structure.\n    ///\n    /// Note: This loses the union/intersection distinction. The returned iterator yields\n    /// all `CallableBinding`s from all elements, which can then be further flattened to\n    /// individual `Binding`s via `CallableBinding`'s `IntoIterator` implementation.\n    pub(crate) fn iter_flat(&self) -> impl Iterator<Item = &CallableBinding<'db>> {\n        self.elements.iter().flat_map(BindingsElement::callables)\n    }\n\n    /// Returns a mutable iterator over all `CallableBinding`s, flattening the two-level structure.\n    ///\n    /// Note: This loses the union/intersection distinction. Use only when you need to\n    /// modify all bindings regardless of their union/intersection grouping.\n    pub(crate) fn iter_flat_mut(&mut self) -> impl Iterator<Item = &mut CallableBinding<'db>> {\n        self.elements\n            .iter_mut()\n            .flat_map(BindingsElement::callables_mut)\n    }\n\n    fn iter_callable_items(&self) -> impl Iterator<Item = &CallableItem<'db>> {\n        self.elements.iter().flat_map(BindingsElement::items)\n    }\n\n    fn iter_callable_items_mut(&mut self) -> impl Iterator<Item = &mut CallableItem<'db>> {\n        self.elements\n            .iter_mut()\n            .flat_map(BindingsElement::items_mut)\n    }\n\n    fn iter_constructor_items(&self) -> impl Iterator<Item = &ConstructorBinding<'db>> {\n        self.iter_callable_items()","sourceCodeStart":981,"sourceCodeEnd":1017,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/call/bind.rs#L981-L1017","documentation":"During overload call inference, an OverloadSet records which overloads matched in a previous fixpoint iteration; visit_overload_set zips those recorded candidates against the bindings visited now and expects the sequences to align. The expect fires when the set of checked bindings differs between iterations - the inference results are not stable at the fixpoint where the OverloadSet was cached.","triggerScenarios":"Overload-heavy call sites, especially recursive or mutually recursive inference, where a later iteration produces a different number or order of bindings than the iteration that built the OverloadSet; commonly caused by nondeterministic iteration order (hash maps, constraint ordering) or missing cache invalidation.","commonSituations":"Determinism regressions in ty: this is exactly the class of bug ty's constraint-order wobble tests are designed to catch; also appears in LSP incremental rechecks after small edits.","solutions":["Re-run the check on the same unchanged file: intermittent panics confirm nondeterministic ordering; note that in the issue","Reduce to the overload call (recursive generic functions are frequent culprits) and file a ty issue with it","As a contributor: reproduce with the constraint-order wobble tooling to find the unstable iteration, then fix the ordering or invalidation rather than the expect"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| check_file(&db, file)));\nmatch result {\n    Ok(diags) => diags,\n    Err(payload) => { log::warn!(\"ty fixpoint panic on {}: {:?}\", file, payload); vec![] }\n}","preventionTips":["Re-run the same file unchanged: intermittent panics indicate nondeterministic ordering - include that fact in the bug report","Keep overload-heavy recursive functions in the repro when minimizing","Contributors: run constraint-order wobble tests when touching overload caching or fixpoint loops"],"tags":["rust","ty","call-inference","overloads","fixpoint","nondeterminism","panic"],"backgroundTag":"fixpoint-iteration-instability","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}