astral-sh/ruff · error
node should be non-terminal
Error message
node should be non-terminal
What it means
During the same owned-set rebuild, every visited non-terminal BDD node must have a support id in storage (`node_support_id`). The traversal loop already skips terminal nodes, so this expect fires only when storage's interior-node data is inconsistent with its node list - corrupted or partially compacted constraint storage, not a user-code condition.
Source
Thrown at crates/ty_python_semantic/src/types/constraints.rs:1111
}
// This is a separate method from `ensure_overlay_identity_caches` because it requires a `db`.
fn ensure_overlay_typevar_identity_cache(&mut self, db: &'db dyn Db) {
let Some(compacted) = &self.compacted else {
return;
};
if !self.typevar_cache.is_empty() {
return;
}
self.typevar_cache.extend(
compacted
.typevars
.iter_enumerated()
.map(|(id, typevar)| (typevar.identity(db), id)),
);
}
fn adjusted_node_id(&self, id: NodeId) -> NodeId {
if let Some(compacted) = &self.compacted {
return id + compacted.node_indices.len();
}
id
}
fn adjusted_constraint_id(&self, id: ConstraintId) -> ConstraintId {
if let Some(compacted) = &self.compacted {
return id + compacted.constraint_indices.len();
}
id
}
fn adjusted_support_id(&self, id: SupportId) -> SupportId {
if let Some(compacted) = &self.compacted {
return id + compacted.support_indices.len();
}View on GitHub (pinned to 15f3fe6b15)
Solutions
- Check the compaction/filter steps keep `nodes`, `supports`, and `node_supports` consistent (the used_nodes/used_supports bookkeeping below this expect).
- Add a storage self-check (debug assert) that every interior node has a support id after each mutation phase.
- Minimize the failing inference fixture and run `cargo nextest run -p ty_python_semantic`.
- Report upstream with the fixture if unmodified ty panics.
Defensive patterns
Strategy: try-catch
Try / catch
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
owned_set.map(db, f)
}))
.unwrap_or_else(|_| OwnedConstraintSet::terminal()); Prevention
- Treat any hit as solver-state corruption: capture the input and backtrace, not just the message.
- Contributors: add storage self-checks (every interior node has a support) behind debug asserts.
- Avoid hand-building constraint storage in tests; go through the public combine operations.
When it happens
Trigger: Running the rebuild over a storage whose node list contains interior nodes without recorded support ids - e.g. a compaction step that filtered nodes but not their support entries, or a hand-built storage in tests.
Common situations: Contributions to the constraint-set compaction/filtering code; adding new node kinds to the BDD; usually hit via mdtests after editing `constraints.rs` rather than in released builds.
Related errors
- non-terminal BDD should have source_order
- storage-free owned constraint sets must have terminal roots
- Expected `NamedTuple` definition r.h.s. to be a call express
- typevar should be interned before ordering
- every TDD constraint should have a source order
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20).
Data as JSON: /api/errors/3ea7f3b7f8f3b4a9.
Report an issue: GitHub.