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

  1. Re-run the check on the same unchanged file: intermittent panics confirm nondeterministic ordering; note that in the issue
  2. Reduce to the overload call (recursive generic functions are frequent culprits) and file a ty issue with it
  3. 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

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


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20). Data as JSON: /api/errors/3261d54ae71212b3. Report an issue: GitHub.