{"record":{"id":"e1f8e88847a76703","repo":"hibernate/hibernate-orm","slug":"action-was-vetoed-entityaction","errorCode":null,"errorMessage":"Action was vetoed: \" + entityAction","messagePattern":"Action was vetoed: \" \\+ entityAction","errorType":"exception","errorClass":"EntityActionVetoException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/action/queue/internal/GraphBasedActionQueue.java","lineNumber":236,"sourceCode":"\t\texecutePendingInserts();\n\n\t\tfinal var nonNullableTransientDeps = insert.findNonNullableTransientEntities();\n\t\tif ( nonNullableTransientDeps != null ) {\n\t\t\tflushCoordinator.getDecomposer().trackUnresolvedInsert( insert, nonNullableTransientDeps );\n\t\t\treturn;\n\t\t}\n\n\t\tACTION_LOGGER.executingIdentityInsertImmediately();\n\t\tinsert.execute();\n\t\tif ( !insert.isVeto() ) {\n\t\t\tinsert.makeEntityManaged();\n\t\t\texecutePendingInserts();\n\t\t\tfor ( var resolvedAction : flushCoordinator.getDecomposer().resolveDependentActions( insert.getInstance() ) ) {\n\t\t\t\taddInsertAction( resolvedAction );\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthrow new EntityActionVetoException( insert );\n\t\t}\n\t\tregisterCleanupActions( insert );\n\t}\n\n\tprivate void addResolvedNonEarlyInsertAction(AbstractEntityInsertAction insert) {\n\t\tACTION_LOGGER.addingResolvedNonEarlyInsertAction();\n\t\t\tif ( !insertions.contains( insert ) ) {\n\t\t\t\tinsertions.add( insert );\n\t\t}\n\t\tmakeEntityManagedAndResolveDependentActions(insert);\n\t}\n\n\tprivate void makeEntityManagedAndResolveDependentActions(AbstractEntityInsertAction insert) {\n\t\tif ( !insert.isVeto() ) {\n\t\t\tinsert.makeEntityManaged();\n\t\t\tfor ( var resolvedAction : flushCoordinator.getDecomposer().resolveDependentActions( insert.getInstance() ) ) {\n\t\t\t\taddInsertAction( resolvedAction );\n\t\t\t}","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/action/queue/internal/GraphBasedActionQueue.java#L218-L254","documentation":"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.","triggerScenarios":"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.","commonSituations":"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').","solutions":["Inspect registered PreInsertEventListeners (Integrator / EventListenerRegistry) and fix the vetoing listener: return true only when the insert must actually be blocked, and log the reason","If veto is the intended behavior, catch EntityActionVetoException in the use case and translate it into a domain response (e.g. 'rejected by policy')","Verify the entity/condition match in the listener - veto firing for the wrong entity usually means an instanceof/null check bug in listener code","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)"],"exampleFix":"// before - listener vetoes everything on any exception, inserts die at flush\npublic class AuditInsertListener implements PreInsertEventListener {\n    public boolean onPreInsert(PreInsertEvent event) {\n        try { check(event); return false; }\n        catch (Exception e) { return true; } // vetoes whole insert silently\n    }\n}\n\n// after - fail loudly instead of vetoing, veto only on explicit policy match\npublic boolean onPreInsert(PreInsertEvent event) {\n    if (isBlockedByPolicy(event)) { LOG.info(\"vetoing {}\", event.getEntityName()); return true; }\n    check(event); // throws on real errors\n    return false;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// treat a veto as a business rejection, not an infrastructure failure\ntry {\n    tx.begin(); em.persist(entity); tx.commit();\n} catch (EntityActionVetoException e) {\n    // a PreInsertEventListener deliberately blocked this insert;\n    // surface 'rejected by policy' and audit which listener fired\n    throw new InsertRejectedException(entity, e.getMessage());\n}","preventionTips":["Audit every registered PreInsertEventListener: true means 'block the insert', not 'event handled'","Log the entity and reason whenever a listener returns true - silent vetoes look like data loss","Scope veto listeners to specific entity types via the EventListenerRegistry instead of applying them globally"],"tags":["event-listeners","veto","insert","flush","hibernate"],"backgroundTag":"listener-vetoed-operation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}