hibernate/hibernate-orm · error · IllegalStateException
Unsupported JdbcOperation type: %s
Error message
Unsupported JdbcOperation type: %s
What it means
Thrown during flush by the graph-based ActionQueue's step executor when a queued FlushOperation's JdbcOperation is neither a PreparableMutationOperation (normal prepared SQL mutation) nor a SelfExecutingUpdateOperation (mutation that executes itself, e.g. soft-delete handling). It is an internal dispatch invariant: the executor only knows how to run those two shapes, so any other JdbcOperation implementation is rejected. In practice the class name in the message points to a custom/third-party mutation operation or an internal Hibernate bug, not to an entity mapping mistake.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/action/queue/internal/exec/AbstractStepExecutor.java:98
final boolean execute = beforeOperationExecution( flushOperation );
// No-op operations: only carry post-execution callback, skip SQL execution
if ( flushOperation.getKind() != MutationKind.NO_OP && execute ) {
final var bindPlan = flushOperation.getBindPlan();
if ( bindPlan.getGeneratedValuesCollector() != null ) {
// we need to execute these without batching
executeWithGeneratedValues( flushOperation );
}
else {
final var jdbcOperation = flushOperation.getJdbcOperation();
if ( jdbcOperation instanceof PreparableMutationOperation preparable ) {
executePreparable( preparable, flushOperation );
}
else if ( jdbcOperation instanceof SelfExecutingUpdateOperation selfExecuting ) {
executeSelfExecuting( selfExecuting, flushOperation );
}
else {
throw new IllegalStateException(
"Unsupported JdbcOperation type: " + jdbcOperation.getClass().getName() );
}
}
}
afterOperationExecution( flushOperation, newlyManagedEntityConsumer, fixupOperationConsumer );
}
}
protected boolean beforeOperationExecution(FlushOperation flushOperation) {
final var preExecutionCallback = flushOperation.getPreExecutionCallback();
if ( preExecutionCallback == null ) {
return true;
}
final boolean execute = preExecutionCallback.beforeExecution( (SessionImplementor) session );
flushOperation.setExecutionSkipped( !execute );
return execute;
}View on GitHub (pinned to fad1729dce)
Solutions
- Inspect the class name in the message - it names the exact rejected JdbcOperation implementation; identify which library produces it and remove or upgrade that integration.
- If the operation comes from an extension that only supports the legacy ordering, set hibernate.flush.queue.type=legacy as a workaround (valid values are 'graph' and 'legacy').
- If you implement MutationOperation yourself, make getJdbcOperation() return a PreparableMutationOperation or a SelfExecutingUpdateOperation.
- If no custom code is involved, capture the failing flush in a minimal test and report it against the Hibernate action-queue component.
Example fix
// before
<persistence-unit>
<properties>
<property name="hibernate.flush.queue.type" value="graph"/>
</properties>
</persistence-unit>
// after - fall back to the legacy action queue while the integration is fixed
<persistence-unit>
<properties>
<property name="hibernate.flush.queue.type" value="legacy"/>
</properties>
</persistence-unit> Defensive patterns
Strategy: try-catch
Try / catch
try {
session.flush();
}
catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Unsupported JdbcOperation type")) {
// identify the integration named in the message, roll back, switch queue type or remove it
tx.rollback();
}
throw e;
} Prevention
- Avoid custom JdbcOperation/MutationOperation implementations; prefer @SQLInsert/@SQLUpdate custom SQL which stays a PreparableMutationOperation
- Smoke-test flush for every entity touched by a mutation extension after upgrading Hibernate major versions
- Pin extensions (soft-delete, auditing, sharding) to versions built for your Hibernate line
When it happens
Trigger: session.flush() or transaction commit runs a FlushOperation whose getJdbcOperation() returns a custom JdbcOperation implementation; typically produced by an integration that generates its own mutation operations, or an extension compiled against a different Hibernate version where the operation SPI differed.
Common situations: Custom OGM-like drivers or audit/history extensions supplying their own MutationOperation; upgrading to the 8.0 graph-based flush queue while an extension only supports the legacy queue; misrouted soft-delete/custom-SQL strategies.
Related errors
- Unbreakable unique update cycle detected for SCC: %s
- Unbreakable cycle detected for SCC: %s
- Composite key breakdown not yet implemented
- There are delayed insert actions before operation as cascade
- Unsupported specification: {specification}
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/61b153e1425e51f5.
Report an issue: GitHub.