{"record":{"id":"cc3bc3d0a11876b3","repo":"hibernate/hibernate-orm","slug":"entity-may-not-be-locked-at-level","errorCode":null,"errorMessage":"Entity '{}' may not be locked at level {}","messagePattern":"Entity '(.+?)' may not be locked at level (.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java","lineNumber":37,"sourceCode":" *\n * @author Scott Marlow\n * @since 3.5\n */\npublic class OptimisticForceIncrementLockingStrategy implements LockingStrategy {\n\tprivate final EntityPersister lockable;\n\tprivate final LockMode lockMode;\n\n\t/**\n\t * Construct locking strategy.\n\t *\n\t * @param lockable The metadata for the entity to be locked.\n\t * @param lockMode Indicates the type of lock to be acquired.\n\t */\n\tpublic OptimisticForceIncrementLockingStrategy(EntityPersister lockable, LockMode lockMode) {\n\t\tthis.lockable = lockable;\n\t\tthis.lockMode = lockMode;\n\t\tif ( lockMode.lessThan( LockMode.OPTIMISTIC_FORCE_INCREMENT ) ) {\n\t\t\tthrow new HibernateException( \"Entity '\" + lockable.getEntityName()\n\t\t\t\t\t\t+ \"' may not be locked at level \" + lockMode );\n\t\t}\n\t\tif ( !lockable.isVersioned() ) {\n\t\t\tthrow new HibernateException( \"Entity '\" + lockable.getEntityName()\n\t\t\t\t\t\t+ \"' has no version and may not be locked at level \" + lockMode);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void lock(Object id, Object version, Object object, int timeout, EventSource session) {\n//\t\tfinal EntityEntry entry = session.getPersistenceContextInternal().getEntry( object );\n\t\t// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.\n\t\tsession.getActionQueue().registerCallback( new EntityIncrementVersionProcess( object ) );\n\t}\n\n\tprotected LockMode getLockMode() {\n\t\treturn lockMode;\n\t}","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java#L19-L55","documentation":"OptimisticForceIncrementLockingStrategy implements locking by scheduling a version increment at transaction commit, which only makes sense for modes at or above OPTIMISTIC_FORCE_INCREMENT. Its constructor throws HibernateException 'Entity '<name>' may not be locked at level <mode>' when lockMode.lessThan(LockMode.OPTIMISTIC_FORCE_INCREMENT). The strategy is normally produced by a dialect's getLockingStrategy, so the exception means that mapping handed a weaker mode (OPTIMISTIC, READ, NONE) to this class.","triggerScenarios":"A dialect (or explicit code) constructs OptimisticForceIncrementLockingStrategy with LockMode.OPTIMISTIC or any weaker mode - e.g. session.lock(entity, LockMode.OPTIMISTIC) where the dialect resolves that mode to the force-increment strategy, or new OptimisticForceIncrementLockingStrategy(persister, LockMode.READ) in custom dialect code. The exception is thrown eagerly at strategy construction.","commonSituations":"Custom dialects overriding getLockingStrategy without re-checking the mode threshold; refactoring lock requests from PESSIMISTIC_* to OPTIMISTIC while the dialect mapping stayed on force-increment; unit tests constructing strategies directly with arbitrary modes.","solutions":["Pass LockMode.OPTIMISTIC_FORCE_INCREMENT (or stronger) wherever this strategy is used - e.g. session.lock(entity, LockMode.OPTIMISTIC_FORCE_INCREMENT)","In a custom dialect, return OptimisticLockingStrategy for OPTIMISTIC and OptimisticForceIncrementLockingStrategy only for OPTIMISTIC_FORCE_INCREMENT and above","Prefer the JPA constants (LockModeType.OPTIMISTIC_FORCE_INCREMENT) so the mode is explicit at call sites"],"exampleFix":"// before\nsession.lock(person, LockMode.OPTIMISTIC); // dialect maps this to force-increment strategy\n\n// after\nsession.lock(person, LockMode.OPTIMISTIC_FORCE_INCREMENT);","handlingStrategy":"validation","validationCode":"// Keep mode and strategy aligned before locking\nstatic LockMode forOptimisticForceIncrement(LockMode m) {\n    return m.lessThan(LockMode.OPTIMISTIC_FORCE_INCREMENT) ? LockMode.OPTIMISTIC_FORCE_INCREMENT : m;\n}\nsession.lock(person, forOptimisticForceIncrement(requestedMode));","typeGuard":null,"tryCatchPattern":"try {\n    session.lock(person, LockMode.OPTIMISTIC_FORCE_INCREMENT);\n}\ncatch (HibernateException e) {\n    if (e.getMessage().contains(\"may not be locked at level\")) {\n        throw new IllegalArgumentException(\"Use OPTIMISTIC_FORCE_INCREMENT or stronger with this strategy\", e);\n    }\n    throw e;\n}","preventionTips":["Use JPA LockModeType.OPTIMISTIC_FORCE_INCREMENT explicitly at call sites","In custom dialects, map modes to strategies with a threshold check (never below OPTIMISTIC_FORCE_INCREMENT for this class)","Prefer built-in dialect mappings over hand-rolled getLockingStrategy overrides"],"tags":["hibernate","locking","optimistic-lock","lock-mode","dialect"],"backgroundTag":"invalid-lock-mode","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}