{"record":{"id":"b4f57492324aa53e","repo":"hibernate/hibernate-orm","slug":"batch-update-failed-batchposition","errorCode":null,"errorMessage":"Batch update failed: ${batchPosition}","messagePattern":"Batch update failed: (.+?)","errorType":"exception","errorClass":"BatchFailedException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/jdbc/Expectations.java","lineNumber":68,"sourceCode":"\t\telse {\n\t\t\treturn supplier.get();\n\t\t}\n\t}\n\n\tstatic CallableStatement toCallableStatement(PreparedStatement statement) {\n\t\tif ( statement instanceof CallableStatement callableStatement ) {\n\t\t\treturn callableStatement;\n\t\t}\n\t\telse {\n\t\t\tthrow new HibernateException( \"Expectation.OutParameter operates exclusively on CallableStatements: \"\n\t\t\t\t\t+ statement.getClass() );\n\t\t}\n\t}\n\n\tstatic void checkBatched(int expectedRowCount, int rowCount, int batchPosition, String sql) {\n\t\tswitch (rowCount) {\n\t\t\tcase EXECUTE_FAILED:\n\t\t\t\tthrow new BatchFailedException( \"Batch update failed: \" + batchPosition );\n\t\t\tcase SUCCESS_NO_INFO:\n\t\t\t\tBATCH_MESSAGE_LOGGER.batchSuccessUnknown( batchPosition );\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif ( expectedRowCount > rowCount ) {\n\t\t\t\t\tthrow new StaleStateException(\n\t\t\t\t\t\t\t\"Batch update returned unexpected row count from update \" + batchPosition\n\t\t\t\t\t\t\t\t\t+ actualVsExpected( expectedRowCount, rowCount )\n\t\t\t\t\t\t\t\t\t+ \" [\" + sql + \"]\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\telse if ( expectedRowCount < rowCount ) {\n\t\t\t\t\tthrow new BatchedTooManyRowsAffectedException(\n\t\t\t\t\t\t\t\"Batch update returned unexpected row count from update \" + batchPosition\n\t\t\t\t\t\t\t\t\t+ actualVsExpected( expectedRowCount, rowCount ),\n\t\t\t\t\t\t\texpectedRowCount, rowCount, batchPosition );\n\t\t\t\t}\n\t\t}","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/jdbc/Expectations.java#L50-L86","documentation":"After executing a JDBC batch, Expectations.checkBatched switches on each entry's row count. Statement.EXECUTE_FAILED (-3) means one batch entry failed and the driver continued; Hibernate then throws BatchFailedException naming only the failing position (batchPosition). The original SQL error is not carried by this exception - the position tells you which statement in the flush batch died.","triggerScenarios":"hibernate.jdbc.batch_size > 0 with batched inserts/updates/deletes where one row violates a constraint (unique, FK, not-null, data too long) or times out, and the JDBC driver reports EXECUTE_FAILED for that entry while continuing the batch.","commonSituations":"Bulk-loading mixed-valid data through flush; one bad row among hundreds; driver-specific batch behavior where the underlying cause is swallowed and only the position survives.","solutions":["Temporarily remove hibernate.jdbc.batch_size (run unbatched) and reproduce - the real SQLException for the offending statement then surfaces directly","Map batchPosition to the data: it identifies the Nth statement of the flushed batch, so inspect the corresponding entity/row for constraint or type errors","Fix the data or schema (unique key, FK, column length) and restore batching"],"exampleFix":"// before\nprops.put(\"hibernate.jdbc.batch_size\", \"50\"); // failure reported as position only\n\n// after (debugging)\nprops.remove(\"hibernate.jdbc.batch_size\"); // unbatched run surfaces the real SQLException\n// then fix the offending row and re-enable batching","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    session.flush();\n} catch (org.hibernate.jdbc.BatchFailedException e) {\n    // e.getMessage() = \"Batch update failed: <position>\" - index into this batch's statements\n    // re-run the same data with hibernate.jdbc.batch_size removed to surface the root SQLException\n    throw new IllegalStateException(\"Batch entry \" + e.getMessage(), e);\n}","preventionTips":["Validate data against schema constraints (lengths, uniques, FKs) before flushing large batches","Keep a switch to disable batching so failures reproduce with full SQL errors","Chunk large batches so a failure localizes to a small set of rows"],"tags":["jdbc","batching","constraint-violation","flush"],"backgroundTag":"jdbc-batch-failure","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}