hibernate/hibernate-orm · error · QueryException

H2 json_table() only supports literal json paths, but got {j

Error message

H2 json_table() only supports literal json paths, but got {jsonPath}

What it means

Beyond the initial transformation, the H2 json_table() emulation also computes the parent read path for columns while building the lateral subquery select. That step again needs the literal path text (to detect array access and to strip a trailing [*]), so a non-literal path expression makes this code path throw during SQL generation, even if earlier checks passed.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java:620

				boolean lateral,
				boolean withOrdinality,
				SqmToSqlAstConverter converter) {
			final JsonTableArguments arguments = JsonTableArguments.extract( sqlAstNodes );
			final Expression jsonDocument = arguments.jsonDocument();
			final String documentPath;
			final ColumnReference columnReference = jsonDocument.getColumnReference();
			if ( columnReference != null ) {
				documentPath = columnReference.getExpressionText();
			}
			else {
				documentPath = tableIdentifierVariable + "_." + "d";
			}

			final String parentPath;
			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 );
				final String jsonPath = isArray ? rawJsonPath.substring( 0, rawJsonPath.length() - 3 ) : rawJsonPath;
				parentPath = H2JsonValueFunction.applyJsonPath( documentPath, true, arguments.isJsonType(), jsonPath, arguments.passingClause() );
			}
			else {
				// We have to assume this is an array
				isArray = true;
				parentPath = documentPath;
			}

			final String parentReadExpression;
			if ( isArray ) {
				parentReadExpression = parentPath + "[" + tableIdentifierVariable + ".x]";
			}
			else {
				parentReadExpression = '(' + parentPath + ')';

View on GitHub (pinned to fad1729dce)

Solutions

  1. Inline the literal path: json_table(d.doc, '$.items[*]' columns(...))
  2. Parameterize inner parts with PASSING while keeping the path string literal
  3. Use a native H2 query for dynamic paths
  4. Guard with a repository-layer check that json_table paths are string literals

Example fix

// before - throws on H2
select t.name from Document d, json_table(d.doc, :path columns(name varchar)) t

// after - literal 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; column read paths are derived from the path text");
    }
}

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("Inline the json_table path as a literal: " + hql, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An H2 json_table() call with a non-literal path expression that reaches the nested transformer - typically the same query that binds the path: json_table(d.doc, :path columns(...)), or a path slot filled with a dynamic expression.

Common situations: Parameterized json_table queries in generic flattening code; refactors that moved a formerly literal path into a bind parameter without updating the H2 tests.

Related errors


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