astral-sh/ruff · error
checked bindings are stable across fixpoint iterations
Error message
checked bindings are stable across fixpoint iterations
What it means
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.
Source
Thrown at crates/ty_python_semantic/src/types/call/bind.rs:999
let mut functions = SmallVec::new();
collect(db, self, &mut functions);
functions.into_iter()
}
/// Returns an iterator over all `CallableBinding`s, flattening the two-level structure.
///
/// Note: This loses the union/intersection distinction. The returned iterator yields
/// all `CallableBinding`s from all elements, which can then be further flattened to
/// individual `Binding`s via `CallableBinding`'s `IntoIterator` implementation.
pub(crate) fn iter_flat(&self) -> impl Iterator<Item = &CallableBinding<'db>> {
self.elements.iter().flat_map(BindingsElement::callables)
}
/// Returns a mutable iterator over all `CallableBinding`s, flattening the two-level structure.
///
/// Note: This loses the union/intersection distinction. Use only when you need to
/// modify all bindings regardless of their union/intersection grouping.
pub(crate) fn iter_flat_mut(&mut self) -> impl Iterator<Item = &mut CallableBinding<'db>> {
self.elements
.iter_mut()
.flat_map(BindingsElement::callables_mut)
}
fn iter_callable_items(&self) -> impl Iterator<Item = &CallableItem<'db>> {
self.elements.iter().flat_map(BindingsElement::items)
}
fn iter_callable_items_mut(&mut self) -> impl Iterator<Item = &mut CallableItem<'db>> {
self.elements
.iter_mut()
.flat_map(BindingsElement::items_mut)
}
fn iter_constructor_items(&self) -> impl Iterator<Item = &ConstructorBinding<'db>> {
self.iter_callable_items()View on GitHub (pinned to 15f3fe6b15)
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
Defensive patterns
Strategy: fallback
Try / catch
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| check_file(&db, file)));
match result {
Ok(diags) => diags,
Err(payload) => { log::warn!("ty fixpoint panic on {}: {:?}", file, payload); vec![] }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- argument index should be valid
- bindings must not be empty
- argument index should be valid
- ParamSpec sub-call should only contain a single CallableBind
- extra use-def data should have been retained
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20).
Data as JSON: /api/errors/3261d54ae71212b3.
Report an issue: GitHub.