hibernate/hibernate-orm · error · UnsupportedOperationException

Spanner does not support no wait.

Error message

Spanner does not support no wait.

What it means

This is the NO_WAIT branch of SpannerPostgreSQLDialect.validateSpannerLockTimeout(): millis == Timeouts.NO_WAIT_MILLI throws UnsupportedOperationException('Spanner does not support no wait.'). Called from getLockingClauseStrategy() during query translation, it rejects the no-wait sentinel because the Spanner PostgreSQL interface has no FOR UPDATE NOWAIT. Positive timeouts and skip-locked are refused by the adjacent branches with their own messages.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/SpannerPostgreSQLDialect.java:538

	@Override
	public LockingClauseStrategy getLockingClauseStrategy(
			QuerySpec querySpec, LockOptions lockOptions) {
		if ( lockOptions == null ) {
			return NON_CLAUSE_STRATEGY;
		}
		validateSpannerLockTimeout( lockOptions.getTimeOut() );
		return super.getLockingClauseStrategy( querySpec, lockOptions );
	}

	private static void validateSpannerLockTimeout(int millis) {
		if ( Timeouts.isRealTimeout( millis ) ) {
			throw new UnsupportedOperationException( "Spanner does not support lock timeout." );
		}
		if ( millis == Timeouts.SKIP_LOCKED_MILLI ) {
			throw new UnsupportedOperationException( "Spanner does not support skip locked." );
		}
		if ( millis == Timeouts.NO_WAIT_MILLI ) {
			throw new UnsupportedOperationException( "Spanner does not support no wait." );
		}
	}

	@Override
	public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
		super.contributeTypes( typeContributions, serviceRegistry );

		final var configurationService = serviceRegistry.requireService( ConfigurationService.class );

		this.useIntegerForPrimaryKey = configurationService.getSetting(
				USE_INTEGER_FOR_PRIMARY_KEY,
				StandardConverters.BOOLEAN,
				false
		);

		this.useEmulator = configurationService.getSetting(
				USE_EMULATOR,
				StandardConverters.BOOLEAN,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Omit the NO_WAIT timeout on Spanner PG — plain PESSIMISTIC_WRITE/FOR UPDATE is supported.
  2. Achieve fail-fast with a short transaction timeout rather than a lock modifier.
  3. Catch UnsupportedOperationException at the locking boundary and retry without the option.
  4. Centralize lock configuration per datasource profile and strip lock.timeout for Spanner.

Example fix

// before
LockOptions lo = new LockOptions(LockMode.PESSIMISTIC_WRITE);
lo.setTimeOut(LockOptions.NO_WAIT);
q.setLockOptions(lo);

// after
q.setLockMode(LockMode.PESSIMISTIC_WRITE); // no timeout modifiers
Defensive patterns

Strategy: validation

Validate before calling

if (lockOptions.getTimeOut() == Timeouts.NO_WAIT_MILLI && dialect instanceof SpannerPostgreSQLDialect) {
  lockOptions.setTimeOut(LockOptions.NO_TIMEOUT);
}

Try / catch

try { return query.getResultList(); }
catch (UnsupportedOperationException e) {
  if (e.getMessage().contains("no wait")) { /* retry with default lock options */ }
  throw e;
}

Prevention

When it happens

Trigger: Pessimistic locking with LockOptions.NO_WAIT / Timeouts.NO_WAIT_MILLI (commonly jakarta.persistence.lock.timeout with the no-wait encoding, or lo.setTimeOut(LockOptions.NO_WAIT)) on a SpannerPostgreSQLDialect connection; validation happens in getLockingClauseStrategy before SQL is emitted.

Common situations: Deadlock-shy codebases that set NO_WAIT globally on PostgreSQL being deployed to Spanner PG; oracle-style fail-fast locking; generic service-layer lock aspects reused across datasources.

Related errors


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