hibernate/hibernate-orm · error · UnsupportedOperationException
Index access is only supported for basic plural and string t
Error message
Index access is only supported for basic plural and string types, but got: {sqmPathType} What it means
Thrown by SqmBasicValuedSimplePath.resolveIndexedAccess when index access (path[selector], HQL "[]" syntax or criteria value(index)) is applied to a basic-valued path whose Java type is neither a basic plural type (e.g. List<String>/String[] element access, handled through the list function branch) nor String (which HQL supports as 1-based character access via substring). Hibernate's SQM model can only translate indexed access for those two shapes, and it refuses the operation at query-interpretation time with an UnsupportedOperationException naming the actual path type.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/SqmBasicValuedSimplePath.java:166
final SqmFunctionRegistry registry = queryEngine.getSqmFunctionRegistry();
if ( sqmPathType instanceof BasicPluralType<?, ?> ) {
return registry.getFunctionDescriptor( "array_get" )
.generateSqmExpression(
asList( this, selector ),
null,
queryEngine
);
}
else if ( getJavaTypeClass( sqmPathType ) == String.class ) {
return registry.getFunctionDescriptor( "substring" )
.generateSqmExpression(
asList( this, selector, nodeBuilder().literal( 1 ) ),
nodeBuilder().getCharacterType(),
queryEngine
);
}
else {
throw new UnsupportedOperationException( "Index access is only supported for basic plural and string types, but got: " + sqmPathType );
}
}
private @Nullable Class<?> getJavaTypeClass(SqmDomainType<T> sqmPathType) {
final SqmBindableType<T> expressible = nodeBuilder().resolveExpressible( sqmPathType );
return expressible == null ? null : expressible.getRelationalJavaType().getJavaTypeClass();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SqmPath
@Override
public @Nonnull BasicJavaType<T> getJavaTypeDescriptor() {
return (BasicJavaType<T>) super.getJavaTypeDescriptor();
}
@NonnullView on GitHub (pinned to fad1729dce)
Solutions
- Change the attribute type to List<String> (or String[]) if positional access is required, since only basic plural list/array types support path[index].
- For String character access, prefer the explicit substring(e.name, i, 1) function over index syntax.
- For maps or sets, restructure the query: use key(m)/value(m) on a map path, 'x' member of e.names for membership tests, or join the @ElementCollection.
- If positional semantics are essential, model the data as a real entity table with an index column and order by it.
Example fix
// before - nickNames is a Set<String>, no index access -> UnsupportedOperationException
List<Person> p = session.createQuery("from Person p where p.nickNames[0] = 'Bob'", Person.class).list();
// after - membership test on an unordered collection
List<Person> p = session.createQuery("from Person p where 'Bob' member of p.nickNames", Person.class).list(); Defensive patterns
Strategy: validation
Validate before calling
// Only List/arrays of basics and String support path[index]
static boolean supportsIndexAccess(jakarta.persistence.metamodel.SingularAttribute<?, ?> attr) {
Class<?> t = attr.getJavaType();
return String.class.equals(t) || t.isArray() || java.util.List.class.isAssignableFrom(t);
} Type guard
static boolean indexAccessibleCollection(java.lang.reflect.Field f) {
Class<?> t = f.getType();
return t.isArray() || java.util.List.class.isAssignableFrom(t); // excludes Set, Map, Collection-only
} Try / catch
try {
return em.createQuery(hql, clazz).getResultList();
} catch (UnsupportedOperationException e) {
if (e.getMessage() != null && e.getMessage().contains("Index access")) {
throw new IllegalArgumentException("Indexed access used on non-list path: " + hql, e);
}
throw e;
} Prevention
- Keep collection attributes typed as List<T> (ordered) when queries index them; avoid Set for indexed access.
- Prefer explicit functions (substring, element via join) over [] syntax for portability.
- Add mapping tests that assert the Java collection type of every indexed attribute.
- When changing a collection mapping, grep queries for 'attrName[' usage.
When it happens
Trigger: HQL like "where e.nickNames[0] = 'Bob'" when nickNames is a Set<String>, Map, or another non-indexed collection of basics; indexing a scalar attribute such as e.age[0]; Criteria API plural path access cb services invoking value(literal) on a path whose element/Java type is not List or String; queries written against String that were later re-pointed at char[] or a Set.
Common situations: The entity field was changed from List<String> to Set<String> (or the @ElementCollection target changed) and indexed HQL kept working in tests only for lists; porting SQL array-subscript habits (col[1]) to HQL where the attribute is not a list; assuming map key access e.map['k'] works through this syntax instead of proper map paths; migrating from Blaze-Persistence array syntax.
Related errors
- Index operator applied to non-plural path '${getNavigablePat
- Entity discriminator cannot be de-referenced
- Entity discriminator cannot be de-referenced
- Could not resolve attribute '%s' of '%s' due to the attribut
- The function {name} is not an aggregate function
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/66246fbe40acaadf.
Report an issue: GitHub.