go-sql-driver/mysql · error
mysql: unsupported isolation level
Error message
mysql: unsupported isolation level: %v
What it means
Raised by mapIsolationLevel (utils.go:803) when beginning a transaction. MySQL only supports four isolation levels (READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE); any other driver.IsolationLevel passed to sql.TxOptions falls through to this error. Called from mysqlConn.BeginTx (connection.go:623) before issuing 'SET TRANSACTION ISOLATION LEVEL'.
Solutions
- Use one of the supported levels: sql.LevelReadUncommitted, sql.LevelReadCommitted, sql.LevelRepeatableRead, or sql.LevelSerializable.
- Pass sql.LevelDefault (0) to BeginTx to use the server's default isolation (REPEATABLE READ).
- If you need Linearizable semantics, enforce it at the application/serialization layer, not via the MySQL driver.
- Audit ORM/framework transaction config to ensure it doesn't inject an unsupported level.
Example fix
// before: unsupported level
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelLinearizable})
// after: use a MySQL-supported level (or default)
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
// or simply
tx, err := db.BeginTx(ctx, nil) Defensive patterns
Strategy: validation
Validate before calling
func supportedByMySQL(level sql.IsolationLevel) bool {
switch level {
case sql.LevelReadUncommitted, sql.LevelReadCommitted,
sql.LevelRepeatableRead, sql.LevelSerializable, sql.LevelDefault:
return true
}
return false
}
// before BeginTx:
if !supportedByMySQL(opts.Isolation) {
opts.Isolation = sql.LevelDefault // or pick a supported level
} Type guard
func isMySQLIsolationLevel(l driver.IsolationLevel) bool {
switch sql.IsolationLevel(l) {
case sql.LevelReadUncommitted, sql.LevelReadCommitted,
sql.LevelRepeatableRead, sql.LevelSerializable, sql.LevelDefault:
return true
}
return false
} Try / catch
tx, err := db.BeginTx(ctx, opts)
if err != nil && strings.Contains(err.Error(), "unsupported isolation level") {
opts.Isolation = sql.LevelDefault
tx, err = db.BeginTx(ctx, opts)
} Prevention
- Restrict transaction code to MySQL's four supported isolation levels plus Default.
- Centralize TxOptions construction so unsupported levels are rejected in one place.
- When porting from PostgreSQL, drop LevelSnapshot/Linearizable usages.
- Map ORMs to an explicit supported level rather than passing user input through.
When it happens
Trigger: Calling db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelLinearizable}) (or LevelSnapshot, or any non-default level other than the four supported ones). The error surfaces directly from BeginTx as the transaction's returned error. Note: LevelDefault is intentionally skipped before mapping, so it does not trigger this.
Common situations: Code ported from PostgreSQL (which supports Snapshot/Serializable-based Linearizable semantics), use of sql.LevelLinearizable (common with spanner-like APIs or test fixtures), generic transaction helpers that pass through arbitrary Isolation levels, or an ORM defaulting to an unsupported level.
Related errors
- strict mode has been removed. See…
- argument count mismatch
- bad value for field: `%c`
- can't convert %T to time.Time
- cannot convert type: %T
AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07).
Data as JSON: /api/errors/eb1195f79e0b815d.
Report an issue: GitHub.
Appendix: source
Thrown at utils.go:803
return nil, errors.New("mysql: driver does not support the use of Named Parameters")
}
dargs[n] = param.Value
}
return dargs, nil
}
func mapIsolationLevel(level driver.IsolationLevel) (string, error) {
switch sql.IsolationLevel(level) {
case sql.LevelRepeatableRead:
return "REPEATABLE READ", nil
case sql.LevelReadCommitted:
return "READ COMMITTED", nil
case sql.LevelReadUncommitted:
return "READ UNCOMMITTED", nil
case sql.LevelSerializable:
return "SERIALIZABLE", nil
default:
return "", fmt.Errorf("mysql: unsupported isolation level: %v", level)
}
}
View on GitHub (pinned to 03d76c7e07)