{"record":{"id":"55225bb5d76e9eea","repo":"astral-sh/ruff","slug":"non-terminal-constraint-set-should-have-a-source-o","errorCode":null,"errorMessage":"non-terminal constraint set should have a source_order","messagePattern":"non-terminal constraint set should have a source_order","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/constraints.rs","lineNumber":1715,"sourceCode":"            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];\n            let if_true = rebuild_node(storage, inner, constraints, cache, old_interior.if_true);\n            let if_uncertain = rebuild_node(\n                storage,\n                inner,\n                constraints,\n                cache,\n                old_interior.if_uncertain,\n            );\n            let if_false = rebuild_node(storage, inner, constraints, cache, old_interior.if_false);\n            let old_constraint_index = inner.retained_constraint_index(old_interior.constraint);\n            let (condition, _) = constraints[old_constraint_index];\n            let remapped = condition.ite_uncertain(storage, if_true, if_uncertain, if_false);\n\n            cache.insert(old_node, remapped);\n            remapped\n        }\n\n        if other.node.is_terminal() {\n            return (other.node, None);\n        }\n        let inner = other\n            .inner\n            .as_ref()\n            .expect(\"storage-free owned constraint sets must have terminal roots\");\n\n        // Restore the saved order of referenced typevars before rebuilding constraints. A stored\n        // `T <= U` can have `U` as its subject and `T` as its lower bound. Interning that subject\n        // first would reverse the original typevar order, causing successive loads to alternate","sourceCodeStart":1697,"sourceCodeEnd":1733,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/constraints.rs#L1697-L1733","documentation":"The source-order counterpart of error 156 during a merge: if the incoming `OwnedConstraintSet` has a non-terminal root, it must also carry a `source_order` tree so the merged builder can map it. The expect fires when the incoming set has a non-terminal node but `source_order` is None.","triggerScenarios":"Merging an owned constraint set constructed non-terminal with source_order None - the same inconsistent-construction family as 152 and 156, surfacing at merge time instead of rebuild time.","commonSituations":"Changes to how owned sets are produced (quantification, caching into Salsa, partial moves out of a set); constraint mdtests with multiple merged sets after a solver refactor.","solutions":["Make the owned-set constructor enforce the pairing: non-terminal node implies both Some(inner) and Some(source_order).","Check the producer path that built the incoming set (e.g. quantifier elimination) threads source_order like it threads the node.","Add a regression test merging the output of every combine operation; run the ty_python_semantic suite.","Report upstream with the fixture if unmodified 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());","preventionTips":["Contributors: the triple (node, inner, source_order) must stay consistent for non-terminal roots; check producers, not the merge.","Keep quantifier/combine operations covered by unit tests that merge their outputs.","Users: pin versions and report reachable panics with reproducers."],"tags":["rust","panic","expect","ty","constraints","owned-set","source-order","merge"],"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"}