{"record":{"id":"a0446b48c6ee5685","repo":"hibernate/hibernate-orm","slug":"can-t-emulate-null-on-error-clause-on-h2","errorCode":null,"errorMessage":"Can't emulate null on error clause on H2","messagePattern":"Can't emulate null on error clause on H2","errorType":"exception","errorClass":"QueryException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java","lineNumber":432,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean rendersIdentifierVariable(List<SqlAstNode> arguments, SessionFactoryImplementor sessionFactory) {\n\t\t// To make our lives simpler when supporting non-column JSON document arguments\n\t\treturn true;\n\t}\n\n\t@Override\n\tprotected void renderJsonTable(\n\t\t\tSqlAppender sqlAppender,\n\t\t\tJsonTableArguments arguments,\n\t\t\tAnonymousTupleTableGroupProducer tupleType,\n\t\t\tString tableIdentifierVariable,\n\t\t\tSqlAstTranslator<?> walker) {\n\t\tif ( arguments.errorBehavior() == JsonTableErrorBehavior.NULL ) {\n\t\t\tthrow new QueryException( \"Can't emulate null on error clause on H2\" );\n\t\t}\n\n\t\tfinal Expression jsonPathExpression = arguments.jsonPath();\n\t\tfinal boolean isArray = isArrayAccess( jsonPathExpression, walker );\n\n\t\tif ( arguments.jsonDocument().getColumnReference() == null ) {\n\t\t\tsqlAppender.append( '(' );\n\t\t}\n\t\tif ( isArray ) {\n\t\t\tsqlAppender.append( \"system_range(1,\" );\n\t\t\tsqlAppender.append( Integer.toString( maximumArraySize ) );\n\t\t\tsqlAppender.append( \") \" );\n\t\t}\n\t\telse {\n\t\t\tsqlAppender.append( \"system_range(1,1) \" );\n\t\t}\n\t\tsqlAppender.append( tableIdentifierVariable );\n\t\tif ( arguments.jsonDocument().getColumnReference() == null ) {","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java#L414-L450","documentation":"H2's json_table() emulation (system_range unnest plus lateral joins) propagates JSON parse and path errors, so only the default ERROR ON ERROR behavior can be emulated. Asking for null on error cannot be rendered, and H2JsonTableFunction.renderJsonTable() throws while the query plan is generated.","triggerScenarios":"HQL on the H2 dialect where the json_table error clause uses NULL: select t.name from Document d, json_table(d.doc, '$' null on error columns(name varchar)) t.","commonSituations":"Defensive NULL ON ERROR written for feeds with possibly malformed documents; H2-based unit tests failing after the clause was added for production databases; switching a test suite from PostgreSQL to H2.","solutions":["Remove null on error - the default ERROR ON ERROR is emulated","Pre-filter invalid documents before the json_table query (e.g. where d.doc is json)","Validate JSON at write time so queries can assume well-formed documents","Run these tests with Testcontainers against a database that supports the clause"],"exampleFix":"// before - throws on H2\nselect t.name from Document d, json_table(d.doc, '$' null on error columns(name varchar)) t\n\n// after - default ERROR ON ERROR; ensure documents are valid\nselect t.name from Document d, json_table(d.doc, '$' columns(name varchar)) t","handlingStrategy":"validation","validationCode":"// Reject NULL ON ERROR for json_table on H2 before execution\nstatic void assertTranslatable(SessionFactory sf, String hql) {\n    if (sf.getJdbcServices().getDialect() instanceof org.hibernate.dialect.H2Dialect\n            && hql.toLowerCase().contains(\"null on error\")) {\n        throw new IllegalArgumentException(\n            \"H2 json_table emulation only supports the default ERROR ON ERROR; pre-validate documents\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return session.createQuery(hql, Object[].class).getResultList();\n} catch (org.hibernate.QueryException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"null on error clause on H2\")) {\n        return session.createQuery(stripClause(hql, \"null on error\"), Object[].class).getResultList();\n    }\n    throw e;\n}","preventionTips":["Validate JSON documents at write time; H2 errors will then never fire","Reserve NULL ON ERROR for dialects verified to support it","Test JSON flattening queries against H2 explicitly if H2 is part of your matrix"],"tags":["hibernate","h2","json","hql","json-table","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"}