{"record":{"id":"db1162cbed742e12","repo":"hibernate/hibernate-orm","slug":"criteria-path-path-getnavigablepath-getfull","errorCode":null,"errorMessage":"Criteria path '\" + path.getNavigablePath().getFullPath() + \"' references a root from a different tree  (criteria nodes may only be used within the same query or subquery in which they were created)","messagePattern":"Criteria path '\" \\+ path\\.getNavigablePath\\(\\)\\.getFullPath\\(\\) \\+ \"' references a root from a different tree  \\(criteria nodes may only be used within the same query or subquery in which they were created\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaRootValidator.java","lineNumber":301,"sourceCode":"\t\tif ( path instanceof SqmTreatedPath<?, ?> treatedPath ) {\n\t\t\treturn findRoot( treatedPath.getWrappedPath() );\n\t\t}\n\t\telse if ( path instanceof SqmRoot<?> root ) {\n\t\t\treturn root;\n\t\t}\n\t\telse {\n\t\t\tfinal var lhs = path.getLhs();\n\t\t\treturn lhs == null ? null : findRoot( lhs );\n\t\t}\n\t}\n\n\tprivate void validateRoot(SqmRoot<?> root, SqmPath<?> path) {\n\t\tfor ( var validRoots : validRootStack ) {\n\t\t\tif ( validRoots.contains( root ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalStateException(\n\t\t\t\t\"Criteria path '\" + path.getNavigablePath().getFullPath() + \"' references a root from a different tree \"\n\t\t\t\t\t+ \" (criteria nodes may only be used within the same query or subquery in which they were created)\"\n\t\t);\n\t}\n}\n","sourceCodeStart":283,"sourceCodeEnd":307,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaRootValidator.java#L283-L307","documentation":"SqmCriteriaRootValidator walks every path in a criteria query and checks that its root is one of the roots of the query (or subquery) currently being validated, tracked on a stack. A path whose root belongs to a different CriteriaQuery/Subquery tree throws IllegalStateException: criteria nodes are single-owner objects and may not be shared across queries, unlike HQL string fragments.","triggerScenarios":"Reusing a static or cached Root<Entity> (e.g. a shared COMMON_ROOT) in multiple query.multiselect/where calls; applying a Predicate built against query A's root to query B; passing a Subquery created from one query into another query's where; helper repositories that accept roots created elsewhere.","commonSituations":"Utility predicate builders that cache Root instances for convenience; refactoring so a shared base-query's root leaks into derived queries; copy-paste between query builders; migrating HQL fragment reuse patterns to criteria where nodes were assumed transferable.","solutions":["Create fresh roots per query: Root<Order> o = query.from(Order.class) inside each builder, and pass paths/predicates as functions of the root (Function<Root<T>, Predicate>) instead of prebuilt nodes.","Delete cached/static Root fields; if you need a canonical shape, cache the shape (columns, conditions as lambdas), not the nodes.","For subqueries, always create them from the owning query (query.subquery(...)) and build paths from the subquery's own from(...)."],"exampleFix":"// before\nprivate static final Root<Order> SHARED = SOME_QUERY.from(Order.class); // reused everywhere\nPredicate p = cb.equal(SHARED.get(\"status\"), \"OPEN\");\nquery2.where(p); // IllegalStateException: root from a different tree\n\n// after\nPredicate p = cb.equal(query2.from(Order.class).get(\"status\"), \"OPEN\");\n// or parameterize builders:\n// Predicate buildOpen(CriteriaBuilder cb, Root<Order> o) { return cb.equal(o.get(\"status\"), \"OPEN\"); }","handlingStrategy":"type-guard","validationCode":"// A path is safe for query Q only if its root is one of Q's roots (or a subquery root of Q)\nboolean pathBelongsTo(CriteriaQuery<?> q, Path<?> path) {\n    Root<?> r = findRoot(path);\n    return r != null && (q.getRoots().contains(r) || isSubqueryRootOf(q, r));\n}","typeGuard":"static Root<?> findRoot(Path<?> p) {\n    Path<?> cur = p;\n    while (cur.getParentPath() != null) cur = cur.getParentPath();\n    return cur instanceof Root<?> r ? r : null;\n}","tryCatchPattern":"try {\n    query.where(predicate);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"different tree\")) { /* rebuild predicate against query.from(...) and retry */ }\n    else throw e;\n}","preventionTips":["Never cache or share Root/Subquery/predicate instances across queries; create them per query.","Express reusable conditions as Function<Root<T>, Predicate> so each query binds its own root.","Delete static Root fields in utility classes; they are guaranteed cross-tree leaks."],"tags":["hibernate","criteria","root","query-tree","node-reuse"],"backgroundTag":"criteria-cross-tree-reference","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}