{"record":{"id":"641f385ef90892e1","repo":"hibernate/hibernate-orm","slug":"can-t-emulate-on-error-clause-on-h2-641f38","errorCode":null,"errorMessage":"Can't emulate on error clause on H2","messagePattern":"Can't emulate on error clause on H2","errorType":"exception","errorClass":"QueryException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonQueryFunction.java","lineNumber":36,"sourceCode":"\n/**\n * H2 json_query function.\n */\npublic class H2JsonQueryFunction extends JsonQueryFunction {\n\n\tpublic H2JsonQueryFunction(TypeConfiguration typeConfiguration) {\n\t\tsuper( typeConfiguration, false, true );\n\t}\n\n\t@Override\n\tprotected void render(\n\t\t\tSqlAppender sqlAppender,\n\t\t\tJsonQueryArguments arguments,\n\t\t\tReturnableType<?> returnType,\n\t\t\tSqlAstTranslator<?> walker) {\n\t\t// Json dereference errors by default if the JSON is invalid\n\t\tif ( arguments.errorBehavior() != null && arguments.errorBehavior() != JsonQueryErrorBehavior.ERROR ) {\n\t\t\tthrow new QueryException( \"Can't emulate on error clause on H2\" );\n\t\t}\n\t\tif ( arguments.emptyBehavior() == JsonQueryEmptyBehavior.ERROR ) {\n\t\t\tthrow new QueryException( \"Can't emulate error on empty clause on H2\" );\n\t\t}\n\t\tappendJsonQuery(\n\t\t\t\tsqlAppender,\n\t\t\t\targuments.jsonDocument(),\n\t\t\t\targuments.isJsonType(),\n\t\t\t\targuments.jsonPath(),\n\t\t\t\targuments.passingClause(),\n\t\t\t\targuments.wrapMode(),\n\t\t\t\targuments.emptyBehavior(),\n\t\t\t\twalker\n\t\t);\n\t}\n\n\tstatic void appendJsonQuery(\n\t\t\tSqlAppender sqlAppender,","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonQueryFunction.java#L18-L54","documentation":"H2 has no native json_query(); Hibernate emulates it with dereference expressions, which error out on invalid JSON documents by nature. Consequently only the default ERROR ON ERROR behavior is emulatable - requesting null on error (or an empty-array/empty-object on-error form) is rejected during SQL rendering.","triggerScenarios":"HQL on the H2 dialect: select json_query(d.doc, '$.tags[*]' null on error) from Document d. The check arguments.errorBehavior() != JsonQueryErrorBehavior.ERROR in H2JsonQueryFunction.render() throws for any non-default error behavior.","commonSituations":"Queries written with Oracle/SQL Server habits where NULL ON ERROR is the defensive default, then executed in H2-based unit tests; adopting Hibernate 6.6+/7.x JSON functions with H2 as the development database.","solutions":["Omit the on error clause - the default ERROR behavior is what the emulation implements","Ensure documents are valid before querying (write-time validation or where d.doc is json)","Run these queries in tests against a production-like database via Testcontainers","Use a native query when lenient error handling is a hard requirement"],"exampleFix":"// before - throws on H2\nselect json_query(d.doc, '$.tags[*]' null on error) from Document d\n\n// after - default ERROR ON ERROR; ensure documents are valid\nselect json_query(d.doc, '$.tags[*]') from Document d","handlingStrategy":"validation","validationCode":"// Reject non-default ON ERROR clauses for json_query on H2 before execution\nstatic void assertTranslatable(SessionFactory sf, String hql) {\n    if (sf.getJdbcServices().getDialect() instanceof org.hibernate.dialect.H2Dialect) {\n        String h = hql.toLowerCase();\n        if (h.contains(\"json_query\")) {\n            int i = h.indexOf(\"on error\");\n            if (i >= 0 && !h.startsWith(\"error on error\", i)) {\n                throw new IllegalArgumentException(\n                    \"H2 json_query only supports the default 'error on error'; remove the clause\");\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return session.createQuery(hql, String.class).getResultList();\n} catch (org.hibernate.QueryException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"on error clause on H2\")) {\n        return session.createQuery(stripClause(hql, \"on error\"), String.class).getResultList();\n    }\n    throw e;\n}","preventionTips":["Omit ON ERROR clauses in portable HQL; they are the least portable part of the SQL/JSON grammar","Validate documents at write time so the default ERROR behavior never triggers","Run JSON-heavy tests against the production dialect via Testcontainers, not only H2"],"tags":["hibernate","h2","json","hql","json-query","sql-dialect","query-exception"],"backgroundTag":"json-on-error-clause-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}