astral-sh/ruff · error
accumulator should not be empty
Error message
accumulator should not be empty
What it means
This routine combines nodes in a balanced tree using a SmallVec accumulator keyed by depth (the comment above it diagrams the a/0, ab/1, abcd/2 shape). The `pop().expect(...)` is guarded by `last().is_some_and(...)`, so it is logically unreachable: it exists to satisfy the Option returned by pop. Hitting it means the accumulator's depth invariants were broken elsewhere in the combining logic.
Source
Thrown at crates/ty_python_semantic/src/types/constraints.rs:3002
/// rather than by a union assembled from several alternatives. Exhausting the shared
/// traversal budget also returns false, leaving the original branch unchanged.
fn is_covered_by(
self,
storage: &ConstraintSetStorage<'_>,
other: Self,
remaining_visits: &mut usize,
) -> bool {
if self == other || self == ALWAYS_FALSE || other == ALWAYS_TRUE {
return true;
}
let Some(remaining) = remaining_visits.checked_sub(1) else {
return false;
};
*remaining_visits = remaining;
let (Node::Interior(left), Node::Interior(right)) = (self.node(), other.node()) else {
return false;
};
let left = storage.interior_node_data(left.node());
let right = storage.interior_node_data(right.node());
match left.constraint.ordering().cmp(&right.constraint.ordering()) {
Ordering::Less => {
left.if_true.is_covered_by(storage, other, remaining_visits)
&& left
.if_uncertain
.is_covered_by(storage, other, remaining_visits)
&& left
.if_false
.is_covered_by(storage, other, remaining_visits)
}
Ordering::Equal => {
left.if_uncertain
.is_covered_by(storage, other, remaining_visits)
&& (left
.if_true
.is_covered_by(storage, right.if_true, remaining_visits)
|| left.if_true.is_covered_by(View on GitHub (pinned to 15f3fe6b15)
Solutions
- Check the depth type (u8) cannot wrap for realistic constraint counts, and that every path through the loop either pops or pushes consistently.
- Replace the expect with a `debug_assert!` plus a `continue`-style recovery only after finding the actual invariant break; do not silence it blindly.
- Minimize the failing constraint workload into a unit test in constraints.rs.
- Report upstream with the reproducer if stock ty panics.
Defensive patterns
Strategy: try-catch
Try / catch
let combined = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
combine_all(db, env, nodes)
}))
.unwrap_or_else(|_| (one, None)); // degrade to the identity element Prevention
- A hit here means an algorithm bug, not bad input: report the constraint count and shape immediately.
- Contributors: keep the accumulator depth invariant documented next to the diagram when editing this loop.
- Chunk very large inference workloads while a fix is pending; depth is tracked in a u8.
When it happens
Trigger: A change to the depth bookkeeping - e.g. depth overflow of the u8 counter at 256+ combinations, combine returning early without popping, or mutations of the accumulator between the guard and the pop - makes the while-guard see an element pop cannot return.
Common situations: Editing the tree-combination code in constraints.rs (operator folding over many nodes); inference mdtests with hundreds of constraints after such edits.
Related errors
- typevar should be interned before ordering
- storage-free owned constraint sets must have terminal roots
- every TDD constraint should have a source order
- non-terminal BDD should have source_order
- node should be non-terminal
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20).
Data as JSON: /api/errors/06703c39e16e58ee.
Report an issue: GitHub.