go-sql-driver/mysql · error

TLS requested but server does not support TLS

Error message

TLS requested but server does not support TLS

What it means

ErrNoTLS is returned during the handshake (packets.go:223) when the client configured TLS (cfg.TLS != nil) but the server's advertised capability flags do not include clientSSL. The driver refuses to silently downgrade to plaintext unless allowFallbackToPlaintext is set, to avoid leaking credentials.

Solutions

  1. Enable SSL on the server (mysqld --ssl, provide cert/key) so it advertises the SSL capability.
  2. If plaintext is acceptable for this hop, add allowFallbackToPlaintext=true to the DSN so the driver downgrades instead of erroring.
  3. If neither is possible, remove the tls parameter and accept a plaintext connection deliberately.
  4. Verify you are reaching the real MySQL port and not a proxy that mangles capabilities.

Example fix

// before
dsn := "user:pass@tcp(10.0.0.5:3306)/db?tls=true"
// -> ErrNoTLS on a server without SSL

// after (option A): let the driver fall back to plaintext safely
dsn := "user:pass@tcp(10.0.0.5:3306)/db?tls=true&allowFallbackToPlaintext=true"
// after (option B): enable SSL on mysqld and keep tls=true
Defensive patterns

Strategy: validation

Validate before calling

// Build the DSN with a deliberate fallback so a non-TLS server does not
// hard-fail, while still preferring TLS.
dsn := "user:pass@tcp(host:3306)/db?tls=true&allowFallbackToPlaintext=true"

Type guard

func isNoTLS(err error) bool {
    return errors.Is(err, mysql.ErrNoTLS)
}

Try / catch

db, err := sql.Open("mysql", dsn)
if err == nil {
    err = db.PingContext(ctx)
}
if errors.Is(err, mysql.ErrNoTLS) {
    // either enable SSL on the server or consciously allow plaintext
    return decideTLSFallback()
}

Prevention

When it happens

Trigger: Opening a connection with a DSN containing tls=true / tls=skip-verify / a named TLS config against a mysqld started without SSL support, a server whose SSL library was compiled out, or a port-forwarded/proxied endpoint that strips the SSL capability bit.

Common situations: Local dev mysqld built without OpenSSL; connecting through a sidecar/proxy that terminates the protocol; test containers started with --skip-ssl; security policy requires TLS but the target cannot provide it.

Understand the failure class

Related errors


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

Appendix: source

Thrown at errors.go:21

// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"errors"
	"fmt"
	"log"
	"os"
)

// Various errors the driver might return. Can change between driver versions.
var (
	ErrInvalidConn       = errors.New("invalid connection")
	ErrMalformPkt        = errors.New("malformed packet")
	ErrNoTLS             = errors.New("TLS requested but server does not support TLS")
	ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
	ErrNativePassword    = errors.New("this user requires mysql native password authentication")
	ErrOldPassword       = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
	ErrUnknownPlugin     = errors.New("this authentication plugin is not supported")
	ErrOldProtocol       = errors.New("MySQL server does not support required protocol 41+")
	ErrPktSync           = errors.New("commands out of sync. You can't run this command now")
	ErrPktSyncMul        = errors.New("commands out of sync. Did you run multiple statements at once?")
	ErrPktTooLarge       = errors.New("packet for query is too large. Try adjusting the `Config.MaxAllowedPacket`")
	ErrBusyBuffer        = errors.New("busy buffer")

	// errBadConnNoWrite is used for connection errors where nothing was sent to the database yet.
	// If this happens first in a function starting a database interaction, it should be replaced by driver.ErrBadConn
	// to trigger a resend. Use mc.markBadConn(err) to do this.
	// See https://github.com/go-sql-driver/mysql/pull/302
	errBadConnNoWrite = errors.New("bad connection")
)

View on GitHub (pinned to 03d76c7e07)