hibernate/hibernate-orm · error · HibernateException

Connection lock-timeout does not accept skip-locked

Error message

Connection lock-timeout does not accept skip-locked

What it means

Hibernate 7's CockroachDB support applies pessimistic-lock timeouts at the JDBC connection level: before the locking statement it executes 'set lock_timeout = N' (via Helper.setLockTimeout). The connection setting can only represent a real duration (or 0 = wait forever), so when the requested timeout is the magic value SKIP_LOCKED (-2 ms, Timeouts.SKIP_LOCKED_MILLI) it throws this HibernateException instead of silently applying wrong semantics. CockroachLockingSupport reports Level.SUPPORTED (not EXTENDED), meaning neither skip-locked nor no-wait is expressible through this path.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/CockroachLockingSupport.java:107

						default -> Timeout.milliseconds( millis );
					};
				},
				connection,
				factory
		);
	}

	@Override
	public void setLockTimeout(
			Timeout timeout,
			Connection connection,
			SessionFactoryImplementor factory) {
		Helper.setLockTimeout(
				timeout,
				(t) -> {
					final int milliseconds = timeout.milliseconds();
					if ( milliseconds == SKIP_LOCKED_MILLI ) {
						throw new HibernateException( "Connection lock-timeout does not accept skip-locked" );
					}
					if ( milliseconds == NO_WAIT_MILLI ) {
						throw new HibernateException( "Connection lock-timeout does not accept no-wait" );
					}
					return milliseconds == WAIT_FOREVER_MILLI ? 0 : milliseconds;
				},
				"set lock_timeout = %s",
				connection,
				factory
		);
	}

}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use a real positive timeout (e.g. lockOptions.setTimeOut(1000) or Timeouts.ONE_SECOND) or WAIT_FOREVER (-1) on CockroachDB
  2. If you need skip-locked semantics, request them at the locking-clause level (FOR UPDATE SKIP LOCKED) rather than through the connection lock_timeout, or use a native query
  3. Guard up front: dialect.getLockingSupport().getConnectionLockTimeoutStrategy().getSupportedLevel() - skip-locked is accepted by no level, no-wait only by Level.EXTENDED
  4. Remove or scope down global lock timeout hints set to -2/0 in persistence.xml or Spring Data JPA hints

Example fix

// before
Map<String, Object> hints = Map.of("jakarta.persistence.lock.timeout", -2); // SKIP_LOCKED
Order o = em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);

// after: a real wait; skip-locked is not expressible via CRDB connection lock_timeout
Map<String, Object> hints = Map.of("jakarta.persistence.lock.timeout", 1000);
Order o = em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);
Defensive patterns

Strategy: validation

Validate before calling

ConnectionLockTimeoutStrategy s = sessionFactory.getJdbcServices().getDialect()
        .getLockingSupport().getConnectionLockTimeoutStrategy();
int millis = lockOptions.getTimeOut();
if (millis == Timeouts.SKIP_LOCKED_MILLI) {
    lockOptions.setTimeOut(50); // skip-locked is never expressible via connection lock_timeout
}

Type guard

static boolean acceptsConnectionTimeout(ConnectionLockTimeoutStrategy s, int millis) {
    if (s.getSupportedLevel() == ConnectionLockTimeoutStrategy.Level.NONE) return false;
    if (millis == Timeouts.SKIP_LOCKED_MILLI) return false;
    return millis != Timeouts.NO_WAIT_MILLI
            || s.getSupportedLevel() == ConnectionLockTimeoutStrategy.Level.EXTENDED;
}

Try / catch

try {
    session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE).timeout(1000)).lock(entity);
} catch (HibernateException e) {
    if (e.getMessage() != null && e.getMessage().contains("lock-timeout does not accept")) {
        // requested magic timeout not supported by this dialect: degrade to a real timeout
    } else { throw e; }
}

Prevention

When it happens

Trigger: session.buildLockRequest(LockOptions.UPGRADE_SKIPLOCKED).lock(entity) or LockOptions.setTimeOut(-2); a query with setLockMode(LockModeType.PESSIMISTIC_WRITE) plus hint 'jakarta.persistence.lock.timeout' (or legacy 'javax.persistence.lock.timeout') set to -2; any Timeouts.SKIP_LOCKED Timeout that reaches CockroachLockingSupport.setLockTimeout when LockTimeoutType.CONNECTION was selected (JdbcSelectWithActions registers LockTimeoutHandler).

Common situations: Porting PostgreSQL pessimistic-locking code to CockroachDB; a global <property name="jakarta.persistence.lock.timeout" value="-2"/> in persistence.xml applied to every lock; code that works on SQL Server (Level.EXTENDED) reused on CockroachDB; upgrading from Hibernate 6 where these timeout values were handled by different machinery.

Understand the failure class

Related errors


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