hibernate/hibernate-orm · error · UnsupportedOperationException

immutable global instance of LockOptions

Error message

immutable global instance of LockOptions

What it means

Hibernate shares a set of pre-built global LockOptions constants (LockOptions.NONE, READ, UPGRADE, and package-private OPTIMISTIC/PESSIMISTIC_* variants, defined around LockOptions.java:650-727). These singletons are flagged immutable, and any mutating setter such as setLockMode (LockOptions.java:207) throws UnsupportedOperationException to stop one caller from corrupting locking behavior for every other session in the JVM. The exception therefore means you are mutating a shared static instance rather than your own LockOptions.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/LockOptions.java:207

	/**
	 * Retrieve the overall lock mode in effect for this set of options.
	 *
	 * @return the overall lock mode
	 */
	public LockMode getLockMode() {
		return lockMode;
	}

	/**
	 * Set the overall {@linkplain LockMode lock mode}. The default is
	 * {@link LockMode#NONE}, that is, no locking at all.
	 *
	 * @param lockMode the new overall lock mode
	 * @return {@code this} for method chaining
	 */
	public LockOptions setLockMode(LockMode lockMode) {
		if ( immutable ) {
			throw new UnsupportedOperationException("immutable global instance of LockOptions");
		}
		if ( lockMode == LockMode.UPGRADE_NOWAIT ) {
			timeout = Timeouts.NO_WAIT_MILLI;
		}
		else if ( lockMode == LockMode.UPGRADE_SKIPLOCKED ) {
			timeout = Timeouts.SKIP_LOCKED_MILLI;
		}
		this.lockMode = lockMode;
		return this;
	}

	/**
	 * The timeout associated with {@code this} options, defining a maximum
	 * amount of time that the database should wait to obtain a pessimistic
	 * lock before returning an error to the client.
	 */
	public Timeout getTimeout() {
		return Timeout.milliseconds( getTimeOut() );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Always build your own instance: new LockOptions(LockMode.PESSIMISTIC_WRITE) or new LockOptions().setLockMode(mode)
  2. In methods that accept an optional LockOptions, treat the argument as read-only and copy before mutating: options = (options == null || options == LockOptions.NONE) ? new LockOptions() : options
  3. Never store or mutate the public static constants; only read them
  4. Search for `.setLockMode(` call sites whose receiver may originate from a static constant or an external API

Example fix

// before - mutates the shared global constant
LockOptions.NONE.setLockMode(LockMode.PESSIMISTIC_WRITE);
session.buildLockRequest(LockOptions.NONE).lock(entity);

// after - use a private mutable instance
LockOptions options = new LockOptions(LockMode.PESSIMISTIC_WRITE);
session.buildLockRequest(options).lock(entity);
Defensive patterns

Strategy: validation

Validate before calling

// never mutate a LockOptions you did not construct
static LockOptions mutable(LockOptions candidate) {
    if (candidate == null || candidate == LockOptions.NONE
            || candidate == LockOptions.READ || candidate == LockOptions.UPGRADE) {
        return new LockOptions();
    }
    return candidate;
}

Type guard

static boolean isMutableLockOptions(LockOptions options) {
    return options != null
        && options != LockOptions.NONE
        && options != LockOptions.READ
        && options != LockOptions.UPGRADE;
}

Prevention

When it happens

Trigger: Calling setLockMode(...) on LockOptions.NONE, LockOptions.READ, or LockOptions.UPGRADE; or on a LockOptions instance that a helper method received as a default parameter (commonly defaulting to LockOptions.NONE) and then customizes in place.

Common situations: Utility methods written as `void query(..., LockOptions options = LockOptions.NONE)` that then call options.setLockMode(...); code that worked on Hibernate 5.x where these constants were mutable shared instances; upgrading to Hibernate 6.2+/7 where the immutability guard was added; caching a returned LockOptions and tuning it later.

Related errors


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