{"record":{"id":"d4a1b261846e814f","repo":"hibernate/hibernate-orm","slug":"connection-lock-timeout-does-not-accept-no-wait-d4a1b2","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/MySQLLockingSupport.java","lineNumber":139,"sourceCode":"\t\t\t\t\t},\n\t\t\t\t\tconnection,\n\t\t\t\t\tfactory\n\t\t\t);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setLockTimeout(Timeout timeout, Connection connection, SessionFactoryImplementor factory) {\n\t\t\tHelper.setLockTimeout(\n\t\t\t\t\ttimeout,\n\t\t\t\t\t(t) -> {\n\t\t\t\t\t\t// see https://dev.mysql.com/doc/refman/8.4/en/innodb-parameters.html#sysvar_innodb_lock_wait_timeout\n\t\t\t\t\t\t// unit: seconds, allowed values: [1, 1073741824]\n\t\t\t\t\t\tfinal int milliseconds = timeout.milliseconds();\n\t\t\t\t\t\tif ( milliseconds == SKIP_LOCKED_MILLI ) {\n\t\t\t\t\t\t\tthrow new HibernateException( \"Connection lock-timeout does not accept skip-locked\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( milliseconds == NO_WAIT_MILLI ) {\n\t\t\t\t\t\t\tthrow new HibernateException( \"Connection lock-timeout does not accept no-wait\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( milliseconds == WAIT_FOREVER_MILLI ) {\n\t\t\t\t\t\t\treturn foreverValue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn (int) Math.ceil( (double) milliseconds / 1000);\n\t\t\t\t\t},\n\t\t\t\t\t\"SET @@SESSION.innodb_lock_wait_timeout = %s\",\n\t\t\t\t\tconnection,\n\t\t\t\t\tfactory\n\t\t\t);\n\t\t}\n\t}\n}\n","sourceCodeStart":121,"sourceCodeEnd":153,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/MySQLLockingSupport.java#L121-L153","documentation":"On MySQL, Hibernate sets pessimistic-lock timeouts through 'SET @@SESSION.innodb_lock_wait_timeout = N'. The variable is in whole seconds with allowed range [1, 1073741824], so the no-wait magic value 0 ms (Timeouts.NO_WAIT_MILLI) has no representable value and MySQLLockingSupport throws this HibernateException instead of rounding to 1 second (which would silently wait instead of failing fast). MySQL reports Level.SUPPORTED, not EXTENDED, so no-wait is never accepted here.","triggerScenarios":"session.buildLockRequest(LockOptions.UPGRADE_NOWAIT).lock(entity) or lockOptions.setTimeOut(0); em.find(id, PESSIMISTIC_WRITE) / query locking with hint 'jakarta.persistence.lock.timeout' = 0; Timeouts.NO_WAIT reaching setLockTimeout on MySQL, MariaDB or TiDB dialects that route timeouts through the connection.","commonSituations":"'Fail fast' locking recipes copied from JPA documentation that set lock.timeout=0; global persistence.xml property 'jakarta.persistence.lock.timeout'=0 applied to all locks; moving an app from SQL Server/Oracle (where no-wait works) to MySQL; library code shared across databases assuming no-wait is universal.","solutions":["Use the smallest real timeout (>= 1 second via innodb_lock_wait_timeout) and handle LockTimeoutException to fail fast, or WAIT_FOREVER (-1) to wait","If true no-wait is required, use a native 'SELECT ... FOR UPDATE NOWAIT' (MySQL 8+) outside the connection-timeout path","Guard with getConnectionLockTimeoutStrategy().getSupportedLevel(): no-wait requires Level.EXTENDED (SQL Server/Sybase only)","Audit and remove lock timeout hints set to 0 in configuration or query hints"],"exampleFix":"// before\nMap<String, Object> hints = Map.of(\"jakarta.persistence.lock.timeout\", 0); // NO_WAIT -> throws\nOrder o = em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);\n\n// after: 1s wait, catch the timeout to emulate no-wait behavior\nMap<String, Object> hints = Map.of(\"jakarta.persistence.lock.timeout\", 1000);\ntry {\n    Order o = em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);\n} catch (PessimisticLockException e) { /* row busy: fail fast */ }","handlingStrategy":"validation","validationCode":"int millis = lockOptions.getTimeOut();\nif (millis <= 0) { // 0 = no-wait, -1 = forever, -2 = skip-locked\n    // only WAIT_FOREVER (-1) or >=1000ms make sense for MySQL connection timeouts\n    lockOptions.setTimeOut(millis == Timeouts.WAIT_FOREVER_MILLI ? -1 : 1000);\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    Order o = 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\", 1000);\n        em.find(Order.class, id, LockModeType.PESSIMISTIC_WRITE, hints);\n    } else { throw e; }\n}","preventionTips":["Do not assume lock.timeout=0 (no-wait) is portable: MySQL rejects it at connection level","Prefer a 1s timeout plus PessimisticLockException handling to emulate fail-fast on MySQL","Use native NOWAIT clauses when true no-wait is required","Review JPA hints copied from documentation before applying them globally"],"tags":["mysql","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"}