{"record":{"id":"c3aad934ef5a2a44","repo":"hibernate/hibernate-orm","slug":"plural-path-getnavigablepath-refers-to-a-co","errorCode":null,"errorMessage":"Plural path '${getNavigablePath()}' refers to a collection and so element attribute '${name}' may not be referenced directly (use element() function)","messagePattern":"Plural path '(.+?)' refers to a collection and so element attribute '(.+?)' may not be referenced directly \\(use element\\(\\) function\\)","errorType":"exception","errorClass":"PathException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/SqmPluralValuedSimplePath.java","lineNumber":129,"sourceCode":"\t@Override\n\tpublic @Nonnull JavaType<C> getJavaTypeDescriptor() {\n\t\treturn getPluralAttribute().getAttributeJavaType();\n\t}\n\n\t@Override\n\tpublic <T> T accept(SemanticQueryWalker<T> walker) {\n\t\treturn walker.visitPluralValuedPath( this );\n\t}\n\n\t@Override\n\tpublic SqmPath<?> resolvePathPart(\n\t\t\tString name,\n\t\t\tboolean isTerminal,\n\t\t\tSqmCreationState creationState) {\n\t\t// this is a reference to a collection outside the from clause\n\t\tfinal var nature = CollectionPart.Nature.fromNameExact( name );\n\t\tif ( nature == null ) {\n\t\t\tthrow new PathException( \"Plural path '\" + getNavigablePath()\n\t\t\t\t\t+ \"' refers to a collection and so element attribute '\" + name\n\t\t\t\t\t+ \"' may not be referenced directly (use element() function)\" );\n\t\t}\n\t\tfinal var sqmPath = get( name, true );\n\t\tcreationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );\n\t\treturn sqmPath;\n\t}\n\n\t@Override\n\tpublic SqmPath<?> resolveIndexedAccess(\n\t\t\tSqmExpression<?> selector,\n\t\t\tboolean isTerminal,\n\t\t\tSqmCreationState creationState) {\n\t\tfinal var pathRegistry = creationState.getCurrentProcessingState().getPathRegistry();\n\t\tfinal String alias = selector.toHqlString();\n\t\tfinal var navigablePath =\n\t\t\t\tgetParentNavigablePath()\n\t\t\t\t\t\t.append( getNavigablePath().getLocalName(), alias )","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/SqmPluralValuedSimplePath.java#L111-L147","documentation":"When an HQL/criteria path navigates through a plural attribute outside the FROM clause, SqmPluralValuedSimplePath.resolvePathPart only accepts the built-in collection-part names resolved by CollectionPart.Nature.fromNameExact (element/index forms). Any other continuation name means you are trying to read an attribute of the collection's *contents* directly from the collection reference, which Hibernate rejects with this PathException, telling you to go through element() instead.","triggerScenarios":"HQL like 'from Order o where o.lines.quantity > 5' where 'lines' is @OneToMany/@ManyToMany/@ElementCollection; 'select p.tags.label from Post p'; criteria code calling root.get(\"lines\").get(\"quantity\") across a plural attribute without a join.","commonSituations":"Porting SQL or JPQL written against a flattened schema; assuming Hibernate auto-creates implicit joins for every collection dereference; forgetting the join alias and re-navigating through the owner; upgrading Hibernate versions where previously accepted implicit collection paths now fail fast.","solutions":["Add an explicit join and reference its alias: 'from Order o join o.lines l where l.quantity > 5'","Use the element() function to expose the collection contents: 'where element(o.lines).quantity > 5'","Rewrite as a subquery when you must not change result cardinality: 'where exists (select 1 from o.lines l where l.quantity > 5)'","If you expected a singular attribute, fix the mapping or the path: the attribute was mapped plural by mistake, or the wrong attribute name is used"],"exampleFix":"// before\nList<Order> orders = session.createQuery(\n    \"from Order o where o.lines.quantity > :min\", Order.class)\n    .setParameter(\"min\", 5).list();\n\n// after\nList<Order> orders = session.createQuery(\n    \"from Order o join o.lines l where l.quantity > :min\", Order.class)\n    .setParameter(\"min\", 5).list();","handlingStrategy":"validation","validationCode":"Attribute<?, ?> attr = managedType.getAttribute(parentName);\nif (attr instanceof jakarta.persistence.metamodel.PluralAttribute) {\n    // must join (or use element()) before navigating to 'childName'\n    throw new IllegalArgumentException(\"Join required before \" + parentName + \".\" + childName);\n}","typeGuard":"static boolean isPluralAttribute(ManagedType<?> type, String name) {\n    return type.getAttribute(name) instanceof jakarta.persistence.metamodel.PluralAttribute;\n}","tryCatchPattern":"try {\n    return session.createQuery(hql, type).list();\n} catch (org.hibernate.query.PathException e) {\n    // rethrow with the offending fragment highlighted for query authors\n    throw new QueryBuildingException(\"Invalid path in: \" + hql, e);\n}","preventionTips":["Always join collections before referencing their elements' attributes in HQL","For dynamically built HQL, lint every multi-segment path against the metamodel and reject segments crossing a PluralAttribute without a join","Keep query tests on the same mapping as production so plural paths fail at build time"],"tags":["hibernate","hql","sqm","path-navigation","collection-mapping","pathexception"],"backgroundTag":"invalid-path-dereference","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}