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
On MySQL, Hibernate applies pessimistic-lock timeouts by executing 'SET @@SESSION.innodb_lock_wait_timeout = N' (MySQLLockingSupport.ConnectionLockTimeoutStrategyImpl). That server variable is measured in whole seconds with a minimum of 1, so the magic value SKIP_LOCKED (-2 ms) cannot be mapped and MySQLLockingSupport throws this HibernateException rather than silently waiting. The strategy reports Level.SUPPORTED (not EXTENDED): skip-locked is never expressible through the connection setting, even though MySQL 8 supports SKIP LOCKED as a locking clause.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/MySQLLockingSupport.java:136
// unit: seconds, allowed values: [1, 1073741824]
final int seconds = resultSet.getInt( 1 );
return seconds == foreverValue ? Timeouts.WAIT_FOREVER : Timeout.seconds( seconds );
},
connection,
factory
);
}
@Override
public void setLockTimeout(Timeout timeout, Connection connection, SessionFactoryImplementor factory) {
Helper.setLockTimeout(
timeout,
(t) -> {
// see https://dev.mysql.com/doc/refman/8.4/en/innodb-parameters.html#sysvar_innodb_lock_wait_timeout
// unit: seconds, allowed values: [1, 1073741824]
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" );
}
if ( milliseconds == WAIT_FOREVER_MILLI ) {
return foreverValue;
}
return (int) Math.ceil( (double) milliseconds / 1000);
},
"SET @@SESSION.innodb_lock_wait_timeout = %s",
connection,
factory
);
}
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Use a real positive timeout in ms (it will be rounded up to seconds) or WAIT_FOREVER (-1) on MySQL
- Express skip-locked through the locking clause instead: rely on LockMode UPGRADE_SKIPLOCKED with a dialect/locking-clause strategy that emits 'FOR UPDATE SKIP LOCKED', or a native query
- Check getConnectionLockTimeoutStrategy().getSupportedLevel() before relying on magic timeout values - only EXTENDED (SQL Server/Sybase) supports no-wait, none support skip-locked
- Scope 'jakarta.persistence.lock.timeout' hints so they are not applied globally with value -2
Example fix
// before
Map<String, Object> hints = Map.of("jakarta.persistence.lock.timeout", -2); // SKIP_LOCKED -> throws
List<Order> orders = em.createQuery(...).setLockMode(LockModeType.PESSIMISTIC_WRITE)
.setHints(hints).getResultList();
// after: short real wait (rounded up to 1s by innodb_lock_wait_timeout)
Map<String, Object> hints = Map.of("jakarta.persistence.lock.timeout", 1000);
List<Order> orders = em.createQuery(...).setLockMode(LockModeType.PESSIMISTIC_WRITE)
.setHints(hints).getResultList(); Defensive patterns
Strategy: validation
Validate before calling
int millis = lockOptions.getTimeOut();
if (millis == Timeouts.SKIP_LOCKED_MILLI
|| (millis == Timeouts.NO_WAIT_MILLI
&& strategy.getSupportedLevel() != ConnectionLockTimeoutStrategy.Level.EXTENDED)) {
lockOptions.setTimeOut(1000); // innodb_lock_wait_timeout works in whole seconds >= 1
} 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 {
query.setLockMode(LockModeType.PESSIMISTIC_WRITE).getResultList();
} catch (HibernateException e) {
if (e.getMessage() != null && e.getMessage().contains("lock-timeout does not accept")) {
// magic timeout rejected by innodb_lock_wait_timeout path: adjust and retry
} else { throw e; }
} Prevention
- Remember innodb_lock_wait_timeout is seconds with minimum 1: no-wait/skip-locked cannot be expressed through it
- Use locking clauses (FOR UPDATE SKIP LOCKED / NOWAIT) or native SQL for those semantics on MySQL 8+
- Keep lock timeout hints out of shared/global config
- Add per-dialect locking tests to CI
When it happens
Trigger: session.buildLockRequest(LockOptions.UPGRADE_SKIPLOCKED).lock(entity) / lockOptions.setTimeOut(-2); em.find(id, PESSIMISTIC_WRITE, hints) or query.setLockMode(PESSIMISTIC_WRITE) with 'jakarta.persistence.lock.timeout' = -2; Timeouts.SKIP_LOCKED reaching setLockTimeout when the dialect uses connection-level timeouts. Also applies to MariaDB/TiDB variants reusing this strategy.
Common situations: Queue/poller code written for PostgreSQL (SELECT ... FOR UPDATE SKIP LOCKED) ported to MySQL with the same LockOptions; global lock timeout hint -2 in persistence.xml or Spring Data JPA repository hints; switching databases without adjusting lock timeout magic values; Hibernate 6 -> 7 upgrades where the timeout plumbing changed.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Connection lock-timeout does not accept skip-locked
- Connection lock-timeout does not accept skip-locked
- Connection lock-timeout does not accept no-wait
- Connection lock-timeout does not accept skip-locked
- Connection lock-timeout does not accept skip-locked
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/6344c64062f9ac41.
Report an issue: GitHub.