{"record":{"id":"623527146d2a082f","repo":"hibernate/hibernate-orm","slug":"connection-lock-timeout-does-not-accept-no-wait-623527","errorCode":null,"errorMessage":"Connection lock-timeout does not accept no-wait","messagePattern":"Connection lock-timeout does not accept no-wait","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/PostgreSQLLockingSupport.java","lineNumber":124,"sourceCode":"\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t\tconnection,\n\t\t\t\tfactory\n\t\t);\n\t}\n\n\t@Override\n\tpublic void setLockTimeout(Timeout timeout, Connection connection, SessionFactoryImplementor factory) {\n\t\tHelper.setLockTimeout(\n\t\t\t\ttimeout,\n\t\t\t\t(t) -> {\n\t\t\t\t\tfinal int milliseconds = timeout.milliseconds();\n\t\t\t\t\tif ( milliseconds == SKIP_LOCKED_MILLI ) {\n\t\t\t\t\t\tthrow new HibernateException( \"Connection lock-timeout does not accept skip-locked\" );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( milliseconds == NO_WAIT_MILLI ) {\n\t\t\t\t\t\tthrow new HibernateException( \"Connection lock-timeout does not accept no-wait\" );\n\t\t\t\t\t}\n\t\t\t\t\treturn milliseconds == WAIT_FOREVER_MILLI\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: milliseconds;\n\t\t\t\t},\n\t\t\t\t\"set local lock_timeout = %s\",\n\t\t\t\tconnection,\n\t\t\t\tfactory\n\t\t);\n\t}\n\n\tprivate static int findUnitStartIndex(String value) {\n\t\tfor ( int i = value.length() - 1; i >= 0; i-- ) {\n\t\t\tif ( Character.isDigit( value.charAt( i ) ) ) {\n\t\t\t\treturn i + 1;\n\t\t\t}\n\t\t}\n\t\treturn -1;","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/PostgreSQLLockingSupport.java#L106-L142","documentation":"On PostgreSQL, Hibernate applies pessimistic-lock timeouts with 'set local lock_timeout = N' (N in milliseconds). Because lock_timeout=0 in PostgreSQL disables the timeout (wait forever), the JPA no-wait magic value 0 ms cannot be mapped without inverting its meaning, so PostgreSQLLockingSupport throws this HibernateException. Level is SUPPORTED, not EXTENDED - no-wait is not accepted through the connection on PostgreSQL (use 'FOR UPDATE NOWAIT' at clause level instead).","triggerScenarios":"session.buildLockRequest(LockOptions.UPGRADE_NOWAIT).lock(entity) / lockOptions.setTimeOut(0); em.find(id, PESSIMISTIC_WRITE, hints) or locking queries with 'jakarta.persistence.lock.timeout' = 0; Timeouts.NO_WAIT reaching setLockTimeout when LockTimeoutType.CONNECTION applies.","commonSituations":"Standard JPA 'no-wait' recipes (lock.timeout=0) that predate Hibernate 7's connection-timeout handling; shared entity/repo code run against both PostgreSQL and SQL Server; global persistence.xml lock timeout property set to 0; documentation-copied hints applied to every pessimistic operation.","solutions":["Use a small real timeout (e.g. 10-100 ms) and treat PessimisticLockException as 'busy'","For genuine no-wait, use 'FOR UPDATE NOWAIT' semantics: the locking clause (LockMode.UPGRADE_NOWAIT via clause strategy) or a native query, not the connection timeout","Verify Level via getSupportedLevel(): only EXTENDED (SQL Server/Sybase) accepts no-wait through the connection","Audit configuration for lock.timeout hints equal to 0"],"exampleFix":"// before\nMap<String, Object> hints = Map.of(\"jakarta.persistence.lock.timeout\", 0); // NO_WAIT -> throws on PG\nOrder o = em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);\n\n// after\nMap<String, Object> hints = Map.of(\"jakarta.persistence.lock.timeout\", 50);\ntry {\n    Order o = em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);\n} catch (PessimisticLockException e) { /* busy -> fail fast */ }","handlingStrategy":"validation","validationCode":"int millis = lockOptions.getTimeOut();\nif (millis == Timeouts.NO_WAIT_MILLI) {\n    // PG lock_timeout=0 means wait forever, so no-wait must come from NOWAIT clause\n    lockOptions.setTimeOut(50);\n}","typeGuard":"static boolean acceptsConnectionTimeout(ConnectionLockTimeoutStrategy s, int millis) {\n    if (s.getSupportedLevel() == ConnectionLockTimeoutStrategy.Level.NONE) return false;\n    if (millis == Timeouts.SKIP_LOCKED_MILLI) return false;\n    return millis != Timeouts.NO_WAIT_MILLI\n            || s.getSupportedLevel() == ConnectionLockTimeoutStrategy.Level.EXTENDED;\n}","tryCatchPattern":"try {\n    em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);\n} catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"does not accept no-wait\")) {\n        hints = Map.of(\"jakarta.persistence.lock.timeout\", 50);\n        try { return em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints); }\n        catch (PessimisticLockException busy) { return null; }\n    }\n    throw e;\n}","preventionTips":["Remember PG lock_timeout=0 disables the timeout: Hibernate rightly refuses no-wait there","Use very small positive timeouts plus PessimisticLockException handling for fail-fast","Use FOR UPDATE NOWAIT (native or clause-level) for genuine no-wait","Keep per-database lock timeout profiles instead of one global hint"],"tags":["postgresql","pessimistic-locking","lock-timeout","no-wait","hibernate"],"backgroundTag":"pessimistic-lock-timeout-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}