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 RejectedExecutionHandlerView on GitHub (pinned to a4d93f4f85)
Solutions
- 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).
- 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).
- 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.
- 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
- Prefer the level NAME over the integer in config files — it is self-documenting and survives constant renumbering confusion.
- If using integers, reference java.sql.Connection.TRANSACTION_* constants instead of hardcoding numbers.
- For vendor-specific levels (e.g. SQL Server SNAPSHOT), use connectionInitSql instead of transactionIsolation.
- Validate isolation values in your config layer at startup (see validationCode) rather than letting the pool throw during initialization.
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
- Driver {} claims to not accept jdbcUrl, {}
- Failed to get driver instance for jdbcUrl={}
- Failed to load class ${className}
- connectionTimeout cannot be less than ${SOFT_TIMEOUT_FLOOR}m
- idleTimeout cannot be negative
AI-assisted analysis of brettwooldridge/HikariCP@a4d93f4f85 (2026-08-14).
Data as JSON: /api/errors/f4ec29dca96dc29f.
Report an issue: GitHub.