{"record":{"id":"25d4f6da067cee9c","repo":"astral-sh/ruff","slug":"storage-free-owned-constraint-sets-must-have-termi","errorCode":null,"errorMessage":"storage-free owned constraint sets must have terminal roots","messagePattern":"storage-free owned constraint sets must have terminal roots","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/constraints.rs","lineNumber":1687,"sourceCode":"            if index < split {\n                let compacted_index = compacted.retained_node_index(node);\n                return Some(compacted.node_supports[compacted_index]);\n            }\n            return Some(self.node_supports[NodeId::from_usize(index - split)]);\n        }\n        Some(self.node_supports[node])\n    }\n\n    fn node_support(&self, node: NodeId) -> Option<&Support> {\n        self.node_support_id(node)\n            .map(|support| self.support_data(support))\n    }\n\n    /// Loads an [`OwnedConstraintSet`] into this storage.\n    fn load(\n        &mut self,\n        db: &'db dyn Db,\n        env: &ProgramEnvironment<'db>,\n        other: &OwnedConstraintSet<'db>,\n    ) -> (NodeId, Option<SourceOrderId>) {\n        fn rebuild_node<'db>(\n            storage: &mut ConstraintSetStorage<'db>,\n            inner: &OwnedConstraintSetInner<'db>,\n            constraints: &[(NodeId, Option<SourceOrderId>)],\n            cache: &mut FxHashMap<NodeId, NodeId>,\n            old_node: NodeId,\n        ) -> NodeId {\n            if old_node.is_terminal() {\n                return old_node;\n            }\n            if let Some(remapped) = cache.get(&old_node) {\n                return *remapped;\n            }\n\n            let old_node_index = inner.retained_node_index(old_node);\n            let old_interior = inner.nodes[old_node_index];","sourceCodeStart":1669,"sourceCodeEnd":1705,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/constraints.rs#L1669-L1705","documentation":"When merging another `OwnedConstraintSet` into a builder, the incoming set's root is checked first: a terminal root needs no data, but a non-terminal root must carry its own inner storage (`other.inner`). The expect fires when an owned set was constructed with a non-terminal node but no inner storage - an inconsistent construction that the merge refuses to handle silently.","triggerScenarios":"Merging an `OwnedConstraintSet` whose `node` is non-terminal while `inner` is None - typically a constructor path or optimization that strips inner storage without also collapsing the root to a terminal node.","commonSituations":"Refactoring `OwnedConstraintSet` construction (caching, serialization, quantifier results); tests that hand-build sets; usually a compile-time-adjacent bug caught by constraint mdtests, not user Python code.","solutions":["Audit all `OwnedConstraintSet` constructors: non-terminal root requires Some(inner); terminal root requires None.","Add a debug assertion in the constructor so misuse fails at creation, with context, instead of at merge time.","Write a unit test that merges sets produced by every public combine operation.","Report upstream with the reproducer if stock ty panics."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let merged = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    builder.extend(db, env, other)\n}))\n.unwrap_or_else(|_| builder.clone_set()); // skip the poisoned merge","preventionTips":["Contributors: assert the node/inner pairing in the OwnedConstraintSet constructor so misuse fails early.","Do not cache partially-moved owned sets; take or clone them wholesale.","Report with the sequence of operations that produced the incoming set."],"tags":["rust","panic","expect","ty","constraints","owned-set","merge","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-14T05:17:10.506Z"}