{"record":{"id":"3ea7f3b7f8f3b4a9","repo":"astral-sh/ruff","slug":"node-should-be-non-terminal","errorCode":null,"errorMessage":"node should be non-terminal","messagePattern":"node should be non-terminal","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/constraints.rs","lineNumber":1111,"sourceCode":"    }\n\n    // This is a separate method from `ensure_overlay_identity_caches` because it requires a `db`.\n    fn ensure_overlay_typevar_identity_cache(&mut self, db: &'db dyn Db) {\n        let Some(compacted) = &self.compacted else {\n            return;\n        };\n        if !self.typevar_cache.is_empty() {\n            return;\n        }\n\n        self.typevar_cache.extend(\n            compacted\n                .typevars\n                .iter_enumerated()\n                .map(|(id, typevar)| (typevar.identity(db), id)),\n        );\n    }\n\n    fn adjusted_node_id(&self, id: NodeId) -> NodeId {\n        if let Some(compacted) = &self.compacted {\n            return id + compacted.node_indices.len();\n        }\n        id\n    }\n\n    fn adjusted_constraint_id(&self, id: ConstraintId) -> ConstraintId {\n        if let Some(compacted) = &self.compacted {\n            return id + compacted.constraint_indices.len();\n        }\n        id\n    }\n\n    fn adjusted_support_id(&self, id: SupportId) -> SupportId {\n        if let Some(compacted) = &self.compacted {\n            return id + compacted.support_indices.len();\n        }","sourceCodeStart":1093,"sourceCodeEnd":1129,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/constraints.rs#L1093-L1129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    owned_set.map(db, f)\n}))\n.unwrap_or_else(|_| OwnedConstraintSet::terminal());","preventionTips":["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."],"tags":["rust","panic","expect","ty","constraints","bdd","storage","invariant"],"backgroundTag":"constraint-solver-invariant-panic","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"}