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

  1. 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
  2. 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
  3. Grep the codebase for LockMode.WRITE and remove/replace every use; it is internal-only
  4. 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

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


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