{"record":{"id":"77cb8d24caeff9af","repo":"hibernate/hibernate-orm","slug":"unexpected-postgresql-lock-timeout-format","errorCode":null,"errorMessage":"Unexpected PostgreSQL lock_timeout format: {}","messagePattern":"Unexpected PostgreSQL lock_timeout format: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/PostgreSQLLockingSupport.java","lineNumber":104,"sourceCode":"\t\t\t\t\t//   * Non-zero values may be returned with units such as:\n\t\t\t\t\t//       - milliseconds: \"500ms\"\n\t\t\t\t\t//       - seconds:      \"3s\"\n\t\t\t\t\t//       - minutes:      \"1min\"\n\t\t\t\t\t//       - hours:        \"1h\"\n\t\t\t\t\t// Therefore, we need to parse this String carefully to reconstruct the correct Timeout.\n\t\t\t\t\tString value = resultSet.getString( 1 );\n\t\t\t\t\tif ( \"0\".equals( value ) ) {\n\t\t\t\t\t\treturn Timeouts.WAIT_FOREVER;\n\t\t\t\t\t}\n\t\t\t\t\tfinal var unitStartIndex = findUnitStartIndex( value );\n\t\t\t\t\tfinal var amount = Integer.parseInt( value, 0, unitStartIndex, 10 );\n\t\t\t\t\treturn switch ( unitStartIndex == -1 ? \"ms\" : value.substring( unitStartIndex ) ) {\n\t\t\t\t\t\tcase \"ms\" -> Timeout.milliseconds( amount );\n\t\t\t\t\t\tcase \"s\" -> Timeout.seconds( amount );\n\t\t\t\t\t\tcase \"min\" -> Timeout.seconds( amount * 60 );\n\t\t\t\t\t\tcase \"h\" -> Timeout.seconds( amount * 3600 );\n\t\t\t\t\t\tcase \"d\" -> Timeout.seconds( amount * 3600 * 24 );\n\t\t\t\t\t\tdefault -> throw new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Unexpected PostgreSQL lock_timeout format: \" + value );\n\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","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/PostgreSQLLockingSupport.java#L86-L122","documentation":"PostgreSQLLockingSupport.getLockTimeout reads the baseline lock timeout by running 'show lock_timeout' and parsing the returned string. It understands '0' (wait forever) plus amounts with units ms, s, min, h, and d; any other unit or format falls through to IllegalArgumentException('Unexpected PostgreSQL lock_timeout format: ...'). PostgreSQL echoes the stored value including units, so a server or pool that configured lock_timeout in an unparsed unit (e.g. microseconds, shown as 'us') makes Hibernate's parser fail.","triggerScenarios":"Someone or something ran \"set lock_timeout = '1us'\" (or any sub-millisecond/odd unit) on the connection or server so SHOW returns e.g. '1us', which finds no case in the unit switch; PostgreSQL-compatible engines or proxies returning nonstandard text for 'show lock_timeout'; any manual change of the server's lock_timeout GUC to a unit outside {ms, s, min, h, d} before a pessimistic lock triggers the baseline read in LockTimeoutHandler.performPreAction.","commonSituations":"Connection-pool connection-init SQL (HikariCP connectionInitSql) or a pooler (PgBouncer) setting lock_timeout with microsecond precision; ops tuning GUCs on RDS/Cloud SQL; PostgreSQL forks/shims that format settings differently than vanilla PG; a DBA script setting unusual units globally.","solutions":["Set lock_timeout using a unit Hibernate parses: run \"set lock_timeout = '250ms'\" (or s/min/h/d) in pool init SQL instead of microsecond values","Check the current value with 'show lock_timeout' on the same connection and normalize it before issuing pessimistic locks","If a pooler/proxy is in play, bypass it to confirm what the server actually returns for SHOW","Upgrade Hibernate - parser coverage may grow; verify against your version's PostgreSQLLockingSupport"],"exampleFix":"# before (pool init or session sets a unit Hibernate does not parse)\nset lock_timeout = '750us';\n\n# after (standard unit)\nset lock_timeout = '750ms';","handlingStrategy":"try-catch","validationCode":"// normalize the setting to a unit Hibernate parses before locking\ntry (var st = connection.createStatement()) {\n    st.execute(\"set lock_timeout = '250ms'\");\n}","typeGuard":"static boolean isParsablePgTimeout(String value) {\n    if (value == null || value.isBlank()) return false;\n    if (value.equals(\"0\")) return true;\n    var m = java.util.regex.Pattern.compile(\"^(\\\\d+)(ms|s|min|h|d)$\").matcher(value);\n    return m.matches();\n}","tryCatchPattern":"try {\n    session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(entity);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Unexpected PostgreSQL lock_timeout format\")) {\n        // reset to a standard unit on this connection, then retry\n        // e.g. execute \"set lock_timeout = '250ms'\" and repeat the lock\n    } else { throw e; }\n}","preventionTips":["In pool init SQL, always set lock_timeout with ms/s/min/h/d units - never microsecond values","Check 'show lock_timeout' output on pooled connections during environment validation","Keep Hibernate up to date so newly used PG units are covered by the parser","Watch out for PG-compatible proxies returning nonstandard SHOW output"],"tags":["postgresql","lock-timeout","parsing","connection-pool","hibernate"],"backgroundTag":"database-setting-parse-failed","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}