{"record":{"id":"66246fbe40acaadf","repo":"hibernate/hibernate-orm","slug":"index-access-is-only-supported-for-basic-plural-an","errorCode":null,"errorMessage":"Index access is only supported for basic plural and string types, but got: {sqmPathType}","messagePattern":"Index access is only supported for basic plural and string types, but got: (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/SqmBasicValuedSimplePath.java","lineNumber":166,"sourceCode":"\t\tfinal SqmFunctionRegistry registry = queryEngine.getSqmFunctionRegistry();\n\t\tif ( sqmPathType instanceof BasicPluralType<?, ?> ) {\n\t\t\treturn registry.getFunctionDescriptor( \"array_get\" )\n\t\t\t\t\t.generateSqmExpression(\n\t\t\t\t\t\t\tasList( this, selector ),\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tqueryEngine\n\t\t\t\t\t);\n\t\t}\n\t\telse if ( getJavaTypeClass( sqmPathType ) == String.class ) {\n\t\t\treturn registry.getFunctionDescriptor( \"substring\" )\n\t\t\t\t\t.generateSqmExpression(\n\t\t\t\t\t\t\tasList( this, selector, nodeBuilder().literal( 1 ) ),\n\t\t\t\t\t\t\tnodeBuilder().getCharacterType(),\n\t\t\t\t\t\t\tqueryEngine\n\t\t\t\t\t);\n\t\t}\n\t\telse {\n\t\t\tthrow new UnsupportedOperationException( \"Index access is only supported for basic plural and string types, but got: \" + sqmPathType );\n\t\t}\n\t}\n\n\tprivate @Nullable Class<?> getJavaTypeClass(SqmDomainType<T> sqmPathType) {\n\t\tfinal SqmBindableType<T> expressible = nodeBuilder().resolveExpressible( sqmPathType );\n\t\treturn expressible == null ? null : expressible.getRelationalJavaType().getJavaTypeClass();\n\t}\n\n\n\t// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\t// SqmPath\n\n\t@Override\n\tpublic @Nonnull BasicJavaType<T> getJavaTypeDescriptor() {\n\t\treturn (BasicJavaType<T>) super.getJavaTypeDescriptor();\n\t}\n\n\t@Nonnull","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/domain/SqmBasicValuedSimplePath.java#L148-L184","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before - nickNames is a Set<String>, no index access -> UnsupportedOperationException\nList<Person> p = session.createQuery(\"from Person p where p.nickNames[0] = 'Bob'\", Person.class).list();\n\n// after - membership test on an unordered collection\nList<Person> p = session.createQuery(\"from Person p where 'Bob' member of p.nickNames\", Person.class).list();","handlingStrategy":"validation","validationCode":"// Only List/arrays of basics and String support path[index]\nstatic boolean supportsIndexAccess(jakarta.persistence.metamodel.SingularAttribute<?, ?> attr) {\n    Class<?> t = attr.getJavaType();\n    return String.class.equals(t) || t.isArray() || java.util.List.class.isAssignableFrom(t);\n}","typeGuard":"static boolean indexAccessibleCollection(java.lang.reflect.Field f) {\n    Class<?> t = f.getType();\n    return t.isArray() || java.util.List.class.isAssignableFrom(t); // excludes Set, Map, Collection-only\n}","tryCatchPattern":"try {\n    return em.createQuery(hql, clazz).getResultList();\n} catch (UnsupportedOperationException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Index access\")) {\n        throw new IllegalArgumentException(\"Indexed access used on non-list path: \" + hql, e);\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["hibernate","hql","sqm","indexed-access","collections"],"backgroundTag":"hql-indexed-access-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}