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 PostgreSQL, Hibernate applies pessimistic-lock timeouts by executing 'set local lock_timeout = N' (milliseconds) via PostgreSQLLockingSupport.setLockTimeout. In PostgreSQL lock_timeout=0 means the timeout is disabled (wait forever), so the SKIP_LOCKED magic value (-2 ms) has no connection-level representation and is rejected with this HibernateException rather than being silently converted. PostgreSQL reports Level.SUPPORTED (not EXTENDED), so skip-locked is never accepted on this path even though PG supports FOR UPDATE SKIP LOCKED as a clause.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/PostgreSQLLockingSupport.java:120
case "h" -> Timeout.seconds( amount * 3600 );
case "d" -> Timeout.seconds( amount * 3600 * 24 );
default -> throw new IllegalArgumentException(
"Unexpected PostgreSQL lock_timeout format: " + value );
};
},
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 local lock_timeout = %s",
connection,
factory
);
}
private static int findUnitStartIndex(String value) {
for ( int i = value.length() - 1; i >= 0; i-- ) {
if ( Character.isDigit( value.charAt( i ) ) ) {View on GitHub (pinned to fad1729dce)
Solutions
- Use a real positive timeout (e.g. 1000 ms) or WAIT_FOREVER (-1) for connection-level timeouts on PostgreSQL
- Get skip-locked semantics from the locking clause: let Hibernate emit 'FOR UPDATE SKIP LOCKED' (LockMode.UPGRADE_SKIPLOCKED applied via the locking-clause strategy) or use a native query, rather than from the lock timeout
- Check getConnectionLockTimeoutStrategy().getSupportedLevel() before passing magic timeout values
- Remove -2 lock timeout hints from shared configuration
Example fix
// before
Map<String, Object> hints = Map.of("jakarta.persistence.lock.timeout", -2);
List<Task> tasks = em.createQuery(select t from Task t ..., Task.class)
.setLockMode(LockModeType.PESSIMISTIC_WRITE).setHints(hints)
.setMaxResults(10).getResultList();
// after: rely on skip-locked via follow-on locking without the connection-timeout hint,
// or use a short real wait
Map<String, Object> hints = Map.of("jakarta.persistence.lock.timeout", 1000); Defensive patterns
Strategy: validation
Validate before calling
int millis = lockOptions.getTimeOut();
if (millis == Timeouts.SKIP_LOCKED_MILLI) {
lockOptions.setTimeOut(1000); // connection lock_timeout cannot express skip-locked on PG
} 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(lockOptions).lock(entity);
} catch (HibernateException e) {
if (e.getMessage() != null && e.getMessage().contains("lock-timeout does not accept")) {
lockOptions.setTimeOut(1000);
session.buildLockRequest(lockOptions).lock(entity);
} else { throw e; }
} Prevention
- On PostgreSQL, obtain skip-locked via the FOR UPDATE SKIP LOCKED clause, not the lock timeout value
- Do not set jakarta.persistence.lock.timeout to -2 in shared config
- Verify dialect support level before using magic timeout values
- Cover locking flows with tests running on the production database
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 routes the timeout through the connection (LockTimeoutHandler registered).
Common situations: Skip-locked poller/queue patterns configured via the JPA lock.timeout hint instead of the locking clause; global 'jakarta.persistence.lock.timeout' = -2 property in persistence.xml; porting code from databases where the hint works; Hibernate 6 -> 7 migrations that now funnel lock timeouts through ConnectionLockTimeoutStrategy.
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 skip-locked
- Connection lock-timeout does not accept no-wait
- Connection lock-timeout does not accept skip-locked
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/9bd2df4a8e7a264a.
Report an issue: GitHub.