hibernate/hibernate-orm · error · UnsupportedOperationException
WRITE is not a valid LockMode as an argument
Error message
WRITE is not a valid LockMode as an argument
What it means
LockMode.WRITE is an internal, legacy lock mode that Hibernate itself historically applied to entities while flushing changes (to force a version increment). It was never meant to be requested by callers, so the deprecated LockMode.toLockOptions() switch (LockMode.java:345) has no LockOptions equivalent for it and throws UnsupportedOperationException instead of silently producing a wrong lock. Any API path that converts a caller-supplied LockMode into LockOptions hits this guard.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/LockMode.java:345
/**
* @return an instance of {@link LockOptions} with this lock mode, and
* all other settings defaulted.
*
* @deprecated With no replacement; {@linkplain LockOptions} is no longer considered an API.
*/
@Deprecated(since = "7", forRemoval = true)
public LockOptions toLockOptions() {
return switch (this) {
case NONE -> new LockOptions();
case READ -> new LockOptions( READ );
case OPTIMISTIC -> new LockOptions( OPTIMISTIC );
case OPTIMISTIC_FORCE_INCREMENT -> new LockOptions( OPTIMISTIC_FORCE_INCREMENT );
case UPGRADE_NOWAIT -> new LockOptions( PESSIMISTIC_WRITE, NO_WAIT_MILLI, PessimisticLockScope.NORMAL, Locking.FollowOn.ALLOW );
case UPGRADE_SKIPLOCKED -> new LockOptions( PESSIMISTIC_WRITE, SKIP_LOCKED_MILLI, PessimisticLockScope.NORMAL, Locking.FollowOn.ALLOW );
case PESSIMISTIC_READ -> new LockOptions( PESSIMISTIC_READ );
case PESSIMISTIC_WRITE -> new LockOptions( PESSIMISTIC_WRITE );
case PESSIMISTIC_FORCE_INCREMENT -> new LockOptions( PESSIMISTIC_FORCE_INCREMENT );
case WRITE -> throw new UnsupportedOperationException( "WRITE is not a valid LockMode as an argument" );
};
}
public boolean isPessimistic() {
return this == PESSIMISTIC_READ
|| this == PESSIMISTIC_WRITE
|| this == PESSIMISTIC_FORCE_INCREMENT
|| this == UPGRADE_NOWAIT
|| this == UPGRADE_SKIPLOCKED;
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Replace LockMode.WRITE with LockMode.PESSIMISTIC_FORCE_INCREMENT if you wanted the write-triggered version bump, or LockMode.PESSIMISTIC_WRITE for a plain SELECT ... FOR UPDATE
- If you only need an immediate pessimistic lock, use session.buildLockRequest(LockOptions) (or JPA LockModeType.PESSIMISTIC_WRITE with EntityManager.lock/find) instead of converting a LockMode
- Grep the codebase for LockMode.WRITE and remove/replace every use; it is internal-only
- If a generic LockMode-to-LockOptions conversion is unavoidable, special-case WRITE by rejecting it with your own IllegalArgumentException before calling toLockOptions()
Example fix
// before LockOptions options = LockMode.WRITE.toLockOptions(); session.buildLockRequest(options).lock(entity); // after - request a real database lock instead of the internal WRITE mode session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(entity);
Defensive patterns
Strategy: validation
Validate before calling
// before any LockMode -> LockOptions conversion
static LockOptions request(LockMode mode) {
if (mode == LockMode.WRITE) {
throw new IllegalArgumentException(
"LockMode.WRITE is internal-only; use PESSIMISTIC_WRITE or PESSIMISTIC_FORCE_INCREMENT");
}
return mode.toLockOptions();
} Type guard
static boolean isRequestableLockMode(LockMode mode) {
return mode != null && mode != LockMode.WRITE;
} Try / catch
try {
options = mode.toLockOptions();
} catch (UnsupportedOperationException e) {
if (e.getMessage() != null && e.getMessage().contains("not a valid LockMode")) {
throw new IllegalArgumentException("Invalid lock mode requested: " + mode, e);
}
throw e;
} Prevention
- Grep for LockMode.WRITE and remove every occurrence; treat it as an Hibernate-internal constant
- Centralize locking decisions in one helper that only accepts requestable modes
- When migrating pre-Hibernate-4 code, map legacy WRITE to PESSIMISTIC_FORCE_INCREMENT or PESSIMISTIC_WRITE explicitly
When it happens
Trigger: Calling LockMode.WRITE.toLockOptions() directly, or passing LockMode.WRITE to an API that internally converts LockMode to LockOptions (e.g. session.get()/load() overloads or buildLockRequest helpers that accept a LockMode and translate it). Generic code that iterates all LockMode.values() and converts each one will also land on the WRITE case.
Common situations: Code migrated verbatim from Hibernate 2.x/3.x where LockMode.WRITE (or its predecessor LockMode.UPGRADE usage patterns) appeared in tutorials; copy-pasted locking snippets; test utilities that exercise every LockMode constant; upgrading to Hibernate 6/7 where toLockOptions() was introduced with this guard.
Related errors
- Lock mode ${lockMode} not valid for locking via 'update' sta
- Entity '{}' may not be locked at level {}
- Entity '{}' may not be locked at level {}
- Entity '{}' may not be locked at level {}
- Lock mode {} not valid for locking via 'update' statement
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/dc873c32ab2bfbcf.
Report an issue: GitHub.