hibernate/hibernate-orm · error · UnsupportedOperationException

Spanner does not support no wait.

Error message

Spanner does not support no wait.

What it means

SpannerDialect.getForUpdateNowaitString() throws UnsupportedOperationException because Cloud Spanner's locking (via its FOR UPDATE support) has no NOWAIT mode — lock waits are governed by the transaction, not a SQL suffix. Hibernate calls this method when a pessimistic lock with a no-wait timeout is requested (LockOptions.NO_WAIT), so the request can never be satisfied on Spanner. Companion overloads for aliases and skip-locked throw likewise.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java:1274

	@Override
	public String getWriteLockString(Timeout timeout) {
		return getWriteLockString( timeout.milliseconds() );
	}

	@Override
	public String getReadLockString(int timeout) {
		validateSpannerLockTimeout( timeout );
		return getForUpdateString();
	}

	@Override
	public String getReadLockString(Timeout timeout) {
		return getReadLockString( timeout.milliseconds() );
	}

	@Override
	public String getForUpdateNowaitString() {
		throw new UnsupportedOperationException( "Spanner does not support no wait." );
	}

	@Override
	public String getForUpdateNowaitString(String aliases) {
		throw new UnsupportedOperationException( "Spanner does not support no wait." );
	}

	@Override
	public String getForUpdateSkipLockedString() {
		throw new UnsupportedOperationException( "Spanner does not support skip locked." );
	}

	@Override
	public String getForUpdateSkipLockedString(String aliases) {
		throw new UnsupportedOperationException( "Spanner does not support skip locked." );
	}

	private static void validateSpannerLockTimeout(int millis) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove the NOWAIT option: use plain LockOptions.NONE timeout (0 milliseconds semantics per Timeouts) so getReadLockString()/getForUpdateString() paths are used, which Spanner supports.
  2. Catch UnsupportedOperationException around the locking call and retry with an ordinary FOR UPDATE, or accept wait semantics.
  3. If fail-fast behavior is required, implement it at the application level (e.g. transaction timeout or a lock table) rather than the SQL suffix.
  4. Verify the dialect in use — SpannerPostgreSQLDialect has the same restriction, so switching PG-dialect code to Spanner PG does not help.

Example fix

// before
LockOptions lo = new LockOptions(LockMode.PESSIMISTIC_WRITE);
lo.setTimeOut(LockOptions.NO_WAIT); // -> getForUpdateNowaitString() throws
session.find(Order.class, id, lo);

// after
LockOptions lo = new LockOptions(LockMode.PESSIMISTIC_WRITE);
// leave timeout unset/none — plain FOR UPDATE is supported
session.find(Order.class, id, lo);
Defensive patterns

Strategy: validation

Validate before calling

boolean spanner = dialect instanceof SpannerDialect || dialect instanceof SpannerPostgreSQLDialect;
if (spanner && (lockOptions.getTimeOut() == LockOptions.NO_WAIT || Timeouts.isNoWaitTimeout(lockOptions.getTimeOut()))) {
  lockOptions.setTimeOut(LockOptions.NO_TIMEOUT); // degrade to plain FOR UPDATE
}

Try / catch

try {
  return session.find(Order.class, id, lockOptions);
} catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("no wait")) {
    LockOptions plain = new LockOptions(lockOptions.getLockMode());
    return session.find(Order.class, id, plain);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling session.lock()/find(id, lockOptions) or setting Query.setLockOptions() with LockOptions.NO_WAIT (timeout = 0 meaning nowait, i.e. Timeouts.NO_WAIT_MILLI) or PESSIMISTIC_WRITE combined with `setTimeOut(LockOptions.NO_WAIT)` while connected through SpannerDialect; also `select ... for update nowait` emulation attempts.

Common situations: Reusing pessimistic-locking code written for PostgreSQL/Oracle (where NOWAIT avoids long waits) against a Spanner deployment; default timeout constants copied into LockOptions; frameworks (e.g. ShedLock-style routines) that request NOWAIT to detect contention.

Related errors


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