go-sql-driver/mysql · error

mysql: unsupported isolation level: %v

Error message

mysql: unsupported isolation level: %v

What it means

Thrown by mapIsolationLevel (utils.go:803) when a transaction's isolation level is not one of the four MySQL supports: ReadUncommitted, ReadCommitted, RepeatableRead, Serializable. MySQL has no equivalent for levels like Snapshot or Linearizable, so the driver cannot map them and rejects the begin.

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 c426bd9379)

Solutions

  1. Use one of: sql.LevelReadUncommitted, sql.LevelReadCommitted, sql.LevelRepeatableRead, sql.LevelSerializable, or leave Isolation unset (LevelDefault).
  2. If you need snapshot-style behavior, use REPEATABLE READ (MySQL's default for InnoDB) or START TRANSACTION WITH CONSISTENT SNAPSHOT via raw exec.
  3. Review your ORM's transaction options mapping to ensure it only requests MySQL-supported levels.

Example fix

// before
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSnapshot})

// after
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
Defensive patterns

Strategy: validation

Validate before calling

// whitelist isolation levels MySQL supports before BeginTx
var mysqlIsolation = map[sql.IsolationLevel]bool{
    sql.LevelReadUncommitted: true,
    sql.LevelReadCommitted:   true,
    sql.LevelRepeatableRead:  true,
    sql.LevelSerializable:   true,
}

func beginTxSafe(db *sql.DB, ctx context.Context, iso sql.IsolationLevel) (*sql.Tx, error) {
    opts := &sql.TxOptions{}
    if iso != sql.LevelDefault {
        if !mysqlIsolation[iso] {
            return nil, fmt.Errorf("isolation %v not supported by mysql", iso)
        }
        opts.Isolation = iso
    }
    return db.BeginTx(ctx, opts)
}

Type guard

// isMySQLIsolationLevel narrows to the levels the driver accepts
func isMySQLIsolationLevel(l sql.IsolationLevel) bool {
    switch l {
    case sql.LevelDefault, sql.LevelReadUncommitted, sql.LevelReadCommitted,
        sql.LevelRepeatableRead, sql.LevelSerializable:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Calling db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSnapshot}) (or any non-default, non-MySQL-supported level). Because LevelDefault is handled implicitly by MySQL, only the four named levels plus the default are accepted.

Common situations: Porting code from PostgreSQL/SQL Server that used LevelSnapshot or LevelLinearizable; an ORM defaulting to an isolation level MySQL doesn't have; copy-paste of TxOptions from another DB's example.

Related errors


AI-assisted analysis of go-sql-driver/mysql@c426bd9379 (2026-08-04). Data as JSON: /data/errors/eb1195f79e0b815d.json. Report an issue: GitHub.