hibernate/hibernate-orm · error · QueryException
Can't emulate on error clause on H2
Error message
Can't emulate on error clause on H2
What it means
H2's json_exists() emulation renders the document/path dereference chain and checks IS NOT NULL; a dereference in H2 raises an error on invalid JSON, so the emulation inherently implements 'error on error'. The TRUE ON ERROR and FALSE ON ERROR variants cannot be produced, and the translator throws when it encounters them.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonExistsFunction.java:31
/**
* H2 json_exists function.
*/
public class H2JsonExistsFunction extends JsonExistsFunction {
public H2JsonExistsFunction(TypeConfiguration typeConfiguration) {
super( typeConfiguration, true, true );
}
@Override
protected void render(
SqlAppender sqlAppender,
JsonExistsArguments arguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
// Json dereference errors by default if the JSON is invalid
if ( arguments.errorBehavior() != null && arguments.errorBehavior() != JsonExistsErrorBehavior.ERROR ) {
throw new QueryException( "Can't emulate on error clause on H2" );
}
final String jsonPath;
try {
jsonPath = walker.getLiteralValue( arguments.jsonPath() );
}
catch (Exception ex) {
throw new QueryException( "H2 json_value only support literal json paths, but got " + arguments.jsonPath() );
}
arguments.jsonDocument().accept( walker );
sqlAppender.appendSql( " is not null and " );
H2JsonValueFunction.renderJsonPath(
sqlAppender,
arguments.jsonDocument(),
arguments.isJsonType(),
walker,
jsonPath,
arguments.passingClause()
);View on GitHub (pinned to fad1729dce)
Solutions
- Drop the on error clause and rely on the default ERROR behavior
- Guard with a JSON validity predicate instead: where d.doc is json, so invalid documents never reach the dereference
- Run these specific tests with Testcontainers against a database that supports the clause (e.g. Oracle or PostgreSQL)
- Catch the underlying database error in application code if a lenient probe is needed
Example fix
// before - throws on H2 select json_exists(d.doc, '$.flags[0]' false on error) from Document d // after - default ERROR; filter invalid documents beforehand select json_exists(d.doc, '$.flags[0]') from Document d where d.doc is json
Defensive patterns
Strategy: validation
Validate before calling
// Reject TRUE/FALSE ON ERROR for json_exists on H2 before execution
static void assertTranslatable(SessionFactory sf, String hql) {
if (sf.getJdbcServices().getDialect() instanceof org.hibernate.dialect.H2Dialect) {
String h = hql.toLowerCase();
if (h.contains("json_exists")
&& (h.contains("true on error") || h.contains("false on error"))) {
throw new IllegalArgumentException(
"H2 cannot emulate 'true/false on error' for json_exists; drop the clause");
}
}
} Try / catch
try {
return session.createQuery(hql, Boolean.class).getResultList();
} catch (org.hibernate.QueryException e) {
if (e.getMessage() != null && e.getMessage().contains("on error clause on H2")) {
// Retry with default ERROR ON ERROR after filtering invalid documents
return session.createQuery(stripClause(hql, "on error"), Boolean.class).getResultList();
}
throw e;
} Prevention
- Use 'where doc is json' as a portable pre-filter instead of TRUE/FALSE ON ERROR probes
- Keep H2 in the test matrix but assert dialect capability before using non-default JSON clauses
- For production-parity JSON tests, prefer Testcontainers over H2 in-memory profiles
When it happens
Trigger: HQL on the H2 dialect: select json_exists(d.doc, '$.flags[0]' false on error) from Document d, or the same with true on error. JsonExistsErrorBehavior.TRUE and FALSE are rejected; the default ERROR (or no clause at all) is accepted.
Common situations: Developers use json_exists(... false on error) as a 'safe' validity probe that works on other databases, then the H2-based unit test (the default in many quickstarts) fails; switching tests from Testcontainers PostgreSQL to fast in-memory H2.
Related errors
- H2 json_value only support literal json paths, but got " + a
- Can't emulate on error clause on H2
- Can't emulate error on empty clause on H2
- Can't emulate null on error clause on H2
- Can't emulate on error clause on H2
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/9b53adce27618365.
Report an issue: GitHub.