hibernate/hibernate-orm · error · IllegalArgumentException
Passed attribute [%s] did not correspond to a joinable refer
Error message
Passed attribute [%s] did not correspond to a joinable reference [%s] relative to %s
What it means
The generic attribute-join builder in AbstractSqmFrom dispatches on the kind of the resolved path source: SqmSingularJoin for singular (association/embedded) attributes and bag/list/map/set builders for the plural kinds. Anything else, i.e. a basic-typed attribute such as a String or Integer column, falls to the final else and throws IllegalArgumentException 'did not correspond to a joinable reference'. Basic values are not joinable in Hibernate criteria.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/AbstractSqmFrom.java:956
private <A> SqmAttributeJoin<T, A> buildAttributeJoin(SqmPathSource<A> joinedPathSource, SqmJoinType joinType, boolean fetched) {
if ( joinedPathSource instanceof SqmSingularPersistentAttribute<?, A> ) {
return buildSingularJoin( (SqmSingularPersistentAttribute<T, A>) joinedPathSource, joinType, fetched );
}
else if ( joinedPathSource instanceof SqmBagPersistentAttribute<?, A> ) {
return buildBagJoin( (SqmBagPersistentAttribute<T, A>) joinedPathSource, joinType, fetched );
}
else if ( joinedPathSource instanceof SqmListPersistentAttribute<?, A> ) {
return buildListJoin( (SqmListPersistentAttribute<T, A>) joinedPathSource, joinType, fetched );
}
else if ( joinedPathSource instanceof SqmMapPersistentAttribute<?, ?, A> ) {
return buildMapJoin( (SqmMapPersistentAttribute<T, ?, A>) joinedPathSource, joinType, fetched );
}
else if ( joinedPathSource instanceof SqmSetPersistentAttribute<?, A> ) {
return buildSetJoin( (SqmSetPersistentAttribute<T, A>) joinedPathSource, joinType, fetched );
}
else {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Passed attribute [%s] did not correspond to a joinable reference [%s] relative to %s",
joinedPathSource.getPathName(),
joinedPathSource,
getNavigablePath()
)
);
}
}
private <A> SqmSingularJoin<T, A> buildSingularJoin(
SqmSingularPersistentAttribute<? super T, A> attribute,
SqmJoinType joinType,
boolean fetched) {
if ( attribute.getPathType() instanceof ManagedDomainType ) {
return new SqmSingularJoin<>(
this,View on GitHub (pinned to fad1729dce)
Solutions
- Only join association attributes (@ManyToOne/@OneToOne/@OneToMany/@ManyToMany/@ElementCollection).
- For basic attributes, use the path in a WHERE predicate (equality, like, in) instead of a join.
- If the attribute should be an association, fix the entity mapping (add the relation annotation) and regenerate the metamodel.
Example fix
// before root.join( root.<String>get( "name" ) ); // basic attribute -> not joinable // after query.where( cb.equal( root.get( "name" ), "Alice" ) );
Defensive patterns
Strategy: type-guard
Type guard
static boolean isJoinable(ManagedType<?> type, String attr) {
Attribute<?, ?> a = type.getAttribute( attr );
if ( a instanceof jakarta.persistence.metamodel.PluralAttribute ) return true;
return a instanceof jakarta.persistence.metamodel.SingularAttribute<?, ?> s
&& ( s.getType() instanceof jakarta.persistence.metamodel EntityType
|| s.getType() instanceof jakarta.persistence.metamodel.EmbeddableType );
} Prevention
- Join only association/embedded attributes; filter basic columns with WHERE predicates.
- After mapping refactors, verify the attribute is still an association before criteria code joins it.
- If a 'join' of a basic column seems necessary, that is usually a missing relation annotation or should be a where condition.
When it happens
Trigger: root.join(root.get("name")) or joining a path whose attribute is a @Basic type (String, Integer, enum, Instant); join(...) on a singular attribute whose type is not an association/embeddable.
Common situations: Assuming any path can be joined like in some SQL dialects; missing @ManyToOne/@OneToMany annotations after refactoring so the attribute is basic; porting JPQL/HQL that referenced the column only in WHERE but the translation accidentally used join.
Related errors
- Passed attribute name [%s] did not correspond to a collectio
- Passed attribute name [%s] did not correspond to a collectio
- Passed attribute name [%s] did not correspond to a collectio
- Illegal empty CTE name
- Illegal CTE name [%s]. Names must start with an alphabetic c
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/11f5bf68a92ef55d.
Report an issue: GitHub.