go-sql-driver/mysql · error

invalid value for TLS config name

Error message

invalid value for TLS config name: %v

What it means

The 'tls' DSN parameter accepts the literals true/false/skip-verify/preferred or the name of a TLS config registered via mysql.RegisterTLSConfig. If the value is none of those literals, the driver treats it as a config name and url.QueryUnescape's it; a bad percent-escape yields this error.

Solutions

  1. Reference a registered config by a clean plain name, e.g. '?tls=custom', after calling mysql.RegisterTLSConfig("custom", cfg).
  2. Use a literal ('true', 'skip-verify', 'preferred') if you only need basic TLS behavior.
  3. Percent-encode any '%' in the name as %25.

Example fix

// before
sql.Open("mysql", "user@tcp(127.0.0.1:3306)/db?tls=custom%2")
// after
sql.Open("mysql", "user@tcp(127.0.0.1:3306)/db?tls=custom")
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the tls value is a literal or decodes as a name.
import (
    "net/url"
    "strings"
)
func validTLSVal(v string) bool {
    switch strings.ToLower(v) {
    case "true","false","skip-verify","preferred":
        return true
    }
    _, err := url.QueryUnescape(v)
    return err == nil
}

Type guard

null

Try / catch

// Parse the DSN before opening to catch tls= decode errors early.
if _, err := mysql.ParseDSN(dsn); err != nil {
    return err
}

Prevention

When it happens

Trigger: A DSN like '?tls=%ZZ' where the value is not a recognized literal and contains a malformed percent-escape. Plain bad references like 'tls=custom' (a valid name) do not trigger this; only decode failures do.

Common situations: Mis-escaping a registered TLS config name, or a generated DSN that introduces a stray '%'.

Understand the failure class

Related errors


AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07). Data as JSON: /api/errors/4a703ca01f8f6687. Report an issue: GitHub.

Appendix: source

Thrown at dsn.go:661

			if err != nil {
				return
			}

		// TLS-Encryption
		case "tls":
			boolValue, isBool := readBool(value)
			if isBool {
				if boolValue {
					cfg.TLSConfig = "true"
				} else {
					cfg.TLSConfig = "false"
				}
			} else if vl := strings.ToLower(value); vl == "skip-verify" || vl == "preferred" {
				cfg.TLSConfig = vl
			} else {
				name, err := url.QueryUnescape(value)
				if err != nil {
					return fmt.Errorf("invalid value for TLS config name: %v", err)
				}
				cfg.TLSConfig = name
			}

		// I/O write Timeout
		case "writeTimeout":
			cfg.WriteTimeout, err = time.ParseDuration(value)
			if err != nil {
				return
			}
		case "maxAllowedPacket":
			cfg.MaxAllowedPacket, err = strconv.Atoi(value)
			if err != nil {
				return
			}

		// Connection attributes
		case "connectionAttributes":

View on GitHub (pinned to 03d76c7e07)