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();
	}

	@Nonnull

View on GitHub (pinned to fad1729dce)

Solutions

  1. Change the attribute type to List<String> (or String[]) if positional access is required, since only basic plural list/array types support path[index].
  2. For String character access, prefer the explicit substring(e.name, i, 1) function over index syntax.
  3. 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.
  4. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/66246fbe40acaadf. Report an issue: GitHub.