hibernate/hibernate-orm · error · QueryException
H2 json_table() only supports literal json paths, but got "
Error message
H2 json_table() only supports literal json paths, but got " + arguments.jsonPath()
What it means
For json_table() on H2, Hibernate rewrites the query with a system_range-based unnest when the path targets an array. Deciding array-vs-object shape requires inspecting the literal path text (isArrayAccess looks for a trailing [*]); when the path argument is not a Literal - typically a bind parameter - the query transformer throws this exception during SQM-to-SQL translation.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java:145
}
private static class JsonTableQueryTransformer implements QueryTransformer {
private final FunctionTableGroup functionTableGroup;
private final JsonTableArguments arguments;
private final int maximumArraySize;
public JsonTableQueryTransformer(FunctionTableGroup functionTableGroup, JsonTableArguments arguments, int maximumArraySize) {
this.functionTableGroup = functionTableGroup;
this.arguments = arguments;
this.maximumArraySize = maximumArraySize;
}
@Override
public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmToSqlAstConverter converter) {
final boolean isArray;
if ( arguments.jsonPath() != null ) {
if ( !( arguments.jsonPath() instanceof Literal literal) ) {
throw new QueryException( "H2 json_table() only supports literal json paths, but got " + arguments.jsonPath() );
}
final String rawJsonPath = (String) literal.getLiteralValue();
isArray = isArrayAccess( rawJsonPath );
}
else {
// We have to assume this is an array
isArray = true;
}
if ( isArray ) {
final TableGroup parentTableGroup = querySpec.getFromClause().queryTableGroups(
tg -> tg.findTableGroupJoin( functionTableGroup ) == null ? null : tg
);
final PredicateContainer predicateContainer;
if ( parentTableGroup != null ) {
predicateContainer = parentTableGroup.findTableGroupJoin( functionTableGroup );
}
else {
predicateContainer = querySpec;View on GitHub (pinned to fad1729dce)
Solutions
- Inline the literal path: json_table(d.doc, '$.items[*]' columns(name varchar))
- Pass variable parts through the PASSING clause inside a literal path: json_table(d.doc, '$.items[$i]' passing :idx as i columns(...))
- Switch tests to Testcontainers so the dialect matches production
- Use a native H2 query for fully dynamic paths
Example fix
// before - throws on H2 select t.name from Document d, json_table(d.doc, :path columns(name varchar)) t // after - literal array path select t.name from Document d, json_table(d.doc, '$.items[*]' columns(name varchar)) t
Defensive patterns
Strategy: validation
Validate before calling
// Heuristic lint: json_table paths must be string literals on H2
private static final Pattern PARAM_TABLE_PATH =
Pattern.compile("(?i)json_table\\s*\\([^,]+,\\s*:\\w+");
static void assertLiteralJsonPaths(String hql) {
if (PARAM_TABLE_PATH.matcher(hql).find()) {
throw new IllegalArgumentException(
"json_table path must be a string literal on H2; the array/object shape must be known at translation time");
}
} Type guard
// Java predicate (type-guard analogue) for Criteria/SQM path expressions
static boolean isLiteralPath(org.hibernate.query.sqm.tree.expression.SqmExpression<?> pathExpr) {
return pathExpr instanceof org.hibernate.query.sqm.tree.expression.SqmLiteral<?>;
} Try / catch
try {
return session.createQuery(hql, Object[].class).getResultList();
} catch (org.hibernate.QueryException e) {
if (e.getMessage() != null && e.getMessage().contains("only supports literal json paths")) {
throw new IllegalArgumentException(
"H2 json_table needs a literal path so it can detect array access; inline it: " + hql, e);
}
throw e;
} Prevention
- Inline json_table paths as literals; the H2 emulation reads the path text to detect '[*]' array access
- Use PASSING for variable parts inside a literal path
- Match test and production dialects (Testcontainers) for queries relying on dialect-specific JSON support
When it happens
Trigger: HQL on H2 with a non-literal json_table path: select t.name from Document d, json_table(d.doc, :path columns(name varchar)) t. The check in JsonTableQueryTransformer.transform() at H2JsonTableFunction.java:145 rejects any path that is not a Literal.
Common situations: Generic 'flatten any JSON path' repository methods parameterized by path; H2 test profiles failing while the production profile on a database that accepts parameter paths passes.
Related errors
- H2 json_table() only supports literal json paths, but got {j
- H2 json_value only support literal json paths, but got " + a
- H2 json_query only support literal json paths, but got " + j
- H2 json_table() only supports literal default expressions, b
- CockroachDB json_value only support literal json paths, but
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f790e9be60be1376.
Report an issue: GitHub.