brettwooldridge/HikariCP · error · IllegalArgumentException

Invalid transaction isolation value: ${transactionIsolationN

Error message

Invalid transaction isolation value: ${transactionIsolationName}

What it means

UtilityElf.getTransactionIsolation(name) resolves the transactionIsolation HikariCP property. It first tries IsolationLevel.valueOf(name.toUpperCase(Locale.ENGLISH)); if that fails it falls back to legacy integer parsing. This exception is thrown when the value parsed as an integer successfully but that integer does not equal any known isolation level id (e.g. 6, 0, or -1 is not matched unless an enum has that id). In other words: the config value was numeric but not one of the recognized java.sql.Connection level constants (2=READ_COMMITTED, 4=REPEATABLE_READ, 8=SERIALIZABLE, 1=READ_UNCOMMITTED).

Source

Thrown at src/main/java/com/zaxxer/hikari/util/UtilityElf.java:220

    */
   public static int getTransactionIsolation(final String transactionIsolationName)
   {
      if (transactionIsolationName != null) {
         try {
            // use the english locale to avoid the infamous turkish locale bug
            final var upperCaseIsolationLevelName = transactionIsolationName.toUpperCase(Locale.ENGLISH);
            return IsolationLevel.valueOf(upperCaseIsolationLevelName).getLevelId();
         } catch (IllegalArgumentException e) {
            // legacy support for passing an integer version of the isolation level
            try {
               final var level = Integer.parseInt(transactionIsolationName);
               for (var iso : IsolationLevel.values()) {
                  if (iso.getLevelId() == level) {
                     return iso.getLevelId();
                  }
               }

               throw new IllegalArgumentException("Invalid transaction isolation value: " + transactionIsolationName);
            }
            catch (NumberFormatException nfe) {
               throw new IllegalArgumentException("Invalid transaction isolation value: " + transactionIsolationName, nfe);
            }
         }
      }

      return -1;
   }

   /**
    * Custom RejectedExecutionHandler that does nothing when a task is rejected.
    *
    * @see java.util.concurrent.RejectedExecutionHandler
    * @see java.util.concurrent.ThreadPoolExecutor
    * @hidden
    */
   public static class CustomDiscardPolicy implements RejectedExecutionHandler

View on GitHub (pinned to a4d93f4f85)

Solutions

  1. Use the standard level NAME instead of a number: transactionIsolation=READ_COMMITTED (also READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE) — names are matched case-insensitively via toUpperCase(Locale.ENGLISH).
  2. If you must use an integer, use one of the java.sql.Connection constants: 1 (TRANSACTION_READ_UNCOMMITTED), 2 (TRANSACTION_READ_COMMITTED), 4 (TRANSACTION_REPEATABLE_READ), 8 (TRANSACTION_SERIALIZABLE).
  3. For vendor-specific isolation levels not in that set (e.g. SQL Server SNAPSHOT), remove transactionIsolation and apply it via connectionInitSql (e.g. 'SET TRANSACTION ISOLATION LEVEL SNAPSHOT') or a ConnectionCustomizer.
  4. Check for typos/whitespace in the property value — 'read-committed' with a hyphen also fails valueOf and then fails the int path with error 42.

Example fix

// before
config.setTransactionIsolation("6"); // not a standard level -> IllegalArgumentException

// after
config.setTransactionIsolation("SERIALIZABLE"); // or "READ_COMMITTED", "REPEATABLE_READ", ...
Defensive patterns

Strategy: validation

Validate before calling

// Validate before calling config.setTransactionIsolation(...) / before pool init
private static final Set<String> VALID_NAMES = Set.of(
    "READ_UNCOMMITTED", "READ_COMMITTED", "REPEATABLE_READ", "SERIALIZABLE");
private static final Set<Integer> VALID_IDS = Set.of(1, 2, 4, 8); // java.sql.Connection constants

static String normalizeIsolation(String raw) {
    if (raw == null) return null;
    String v = raw.trim().toUpperCase(Locale.ENGLISH);
    if (VALID_NAMES.contains(v)) return v;
    try {
        int n = Integer.parseInt(v);
        if (VALID_IDS.contains(n)) return v;
    } catch (NumberFormatException ignored) { }
    throw new IllegalArgumentException(
        "transactionIsolation must be one of " + VALID_NAMES + " or one of " + VALID_IDS + ", got: " + raw);
}

Try / catch

// If isolation comes from external config at runtime
try {
    config.setTransactionIsolation(rawValue);
} catch (IllegalArgumentException e) {
    throw new ConfigurationError("Invalid transactionIsolation '" + rawValue
        + "': use READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE, or 1/2/4/8", e);
}

Prevention

When it happens

Trigger: Setting transactionIsolation=6 or any integer other than 1/2/4/8 in HikariConfig; passing a numeric string like "8 " (leading/trailing whitespace usually survives parseInt=ok but 8 matches; values like "0" or "-1" fail the loop); calling HikariConfig.setTransactionIsolation("5") programmatically; tools that generate the config with a computed isolation integer that is not a standard level.

Common situations: Copy-pasting a database-vendor isolation constant (e.g. a driver-specific level like SQL Server's SNAPSHOT or Oracle's 8 vs vendor codes) into transactionIsolation; converting a GUI/container isolation dropdown that emits non-standard numbers; mixing up transactionIsolation (expects name or standard int) with a custom connection-init-sql; Spring's spring.datasource.hikari.transaction-isolation set to a vendor-specific numeric level.

Related errors


AI-assisted analysis of brettwooldridge/HikariCP@a4d93f4f85 (2026-08-14). Data as JSON: /api/errors/f4ec29dca96dc29f. Report an issue: GitHub.