hibernate/hibernate-orm · error · EntityActionVetoException

Action was vetoed: " + entityAction

Error message

Action was vetoed: " + entityAction

What it means

In the graph-based ActionQueue (default since Hibernate 8, hibernate.flush.queue.type=graph), early IDENTITY inserts are executed immediately inside addInsertAction (GraphBasedActionQueue.java:236). After execution it checks isVeto(): if a registered PreInsertEventListener returned true (veto), the queue throws EntityActionVetoException ("Action was vetoed: " + entityAction, EntityActionVetoException.java:32). The insert was skipped by a listener - typically a custom security/validation listener - and the queue surfaces the veto as an exception instead of silently dropping the row.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/action/queue/internal/GraphBasedActionQueue.java:236

		executePendingInserts();

		final var nonNullableTransientDeps = insert.findNonNullableTransientEntities();
		if ( nonNullableTransientDeps != null ) {
			flushCoordinator.getDecomposer().trackUnresolvedInsert( insert, nonNullableTransientDeps );
			return;
		}

		ACTION_LOGGER.executingIdentityInsertImmediately();
		insert.execute();
		if ( !insert.isVeto() ) {
			insert.makeEntityManaged();
			executePendingInserts();
			for ( var resolvedAction : flushCoordinator.getDecomposer().resolveDependentActions( insert.getInstance() ) ) {
				addInsertAction( resolvedAction );
			}
		}
		else {
			throw new EntityActionVetoException( insert );
		}
		registerCleanupActions( insert );
	}

	private void addResolvedNonEarlyInsertAction(AbstractEntityInsertAction insert) {
		ACTION_LOGGER.addingResolvedNonEarlyInsertAction();
			if ( !insertions.contains( insert ) ) {
				insertions.add( insert );
		}
		makeEntityManagedAndResolveDependentActions(insert);
	}

	private void makeEntityManagedAndResolveDependentActions(AbstractEntityInsertAction insert) {
		if ( !insert.isVeto() ) {
			insert.makeEntityManaged();
			for ( var resolvedAction : flushCoordinator.getDecomposer().resolveDependentActions( insert.getInstance() ) ) {
				addInsertAction( resolvedAction );
			}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Inspect registered PreInsertEventListeners (Integrator / EventListenerRegistry) and fix the vetoing listener: return true only when the insert must actually be blocked, and log the reason
  2. If veto is the intended behavior, catch EntityActionVetoException in the use case and translate it into a domain response (e.g. 'rejected by policy')
  3. Verify the entity/condition match in the listener - veto firing for the wrong entity usually means an instanceof/null check bug in listener code
  4. As a diagnostic, temporarily set hibernate.flush.queue.type=legacy to confirm the same listener vetoes there too (it will drop the row rather than throw)

Example fix

// before - listener vetoes everything on any exception, inserts die at flush
public class AuditInsertListener implements PreInsertEventListener {
    public boolean onPreInsert(PreInsertEvent event) {
        try { check(event); return false; }
        catch (Exception e) { return true; } // vetoes whole insert silently
    }
}

// after - fail loudly instead of vetoing, veto only on explicit policy match
public boolean onPreInsert(PreInsertEvent event) {
    if (isBlockedByPolicy(event)) { LOG.info("vetoing {}", event.getEntityName()); return true; }
    check(event); // throws on real errors
    return false;
}
Defensive patterns

Strategy: try-catch

Try / catch

// treat a veto as a business rejection, not an infrastructure failure
try {
    tx.begin(); em.persist(entity); tx.commit();
} catch (EntityActionVetoException e) {
    // a PreInsertEventListener deliberately blocked this insert;
    // surface 'rejected by policy' and audit which listener fired
    throw new InsertRejectedException(entity, e.getMessage());
}

Prevention

When it happens

Trigger: A registered org.hibernate.event.spi.PreInsertEventListener whose onPreInsert returns true for your entity (validation failure, permission check, feature flag), combined with an entity using GenerationType.IDENTITY so its insert takes the early/immediate path in the graph queue; enabling a listener at runtime or via @EntityListeners integration that votes veto.

Common situations: Custom compliance or tenant-permission listeners designed to block inserts of certain entities; test listeners that veto unexpectedly after refactoring; misconfigured listener returning true on error paths instead of throwing; misreading the veto contract (true = block, not 'handled').

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/e1f8e88847a76703. Report an issue: GitHub.