go-sql-driver/mysql · critical

unexpected resp from server for caching_sha2_password, perfo

Error message

unexpected resp from server for caching_sha2_password, perform full authentication

What it means

Thrown in the caching_sha2_password full-auth path (auth.go:426) when, after the client requests the server's RSA public key, the first byte of the response is not iAuthMoreData (0x01). The client expected the server to send its public key; getting a different leading byte means the protocol state is unexpected — typically the server sent an error or an auth-result packet instead.

Source

Thrown at auth.go:426

					pubKey := mc.cfg.pubKey
					if pubKey == nil {
						// request public key from server
						data, err := mc.buf.takeSmallBuffer(4 + 1)
						if err != nil {
							return err
						}
						data[4] = cachingSha2PasswordRequestPublicKey
						err = mc.writePacket(data)
						if err != nil {
							return err
						}

						if data, err = mc.readPacket(); err != nil {
							return err
						}

						if data[0] != iAuthMoreData {
							return fmt.Errorf("unexpected resp from server for caching_sha2_password, perform full authentication")
						}

						// parse public key
						block, rest := pem.Decode(data[1:])
						if block == nil {
							return fmt.Errorf("no pem data found, data: %s", rest)
						}
						pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
						if err != nil {
							return err
						}
						pubKey = pkix.(*rsa.PublicKey)
					}

					// send encrypted password
					err = mc.sendEncryptedPassword(oldAuthData, pubKey)
					if err != nil {
						return err

View on GitHub (pinned to c426bd9379)

Solutions

  1. Enable TLS in the DSN (tls=true or a registered tls config) so the driver uses cleartext auth over an encrypted channel instead of requesting a public key.
  2. Use a unix socket (net=unix) which also takes the cleartext-auth fast path.
  3. Provide the server RSA public key via the DSN (allowOldPasswords / server pub key registration) to skip the request step.
  4. Verify the server actually supports caching_sha2_password and the account is not locked/expired; test the same credentials in the mysql CLI.

Example fix

// before: plain TCP, no TLS
// dsn := "user:pass@tcp(host:3306)/db"

// after: enable TLS so full auth uses cleartext over encrypted channel
dsn := "user:pass@tcp(host:3306)/db?tls=true"
db, err := sql.Open("mysql", dsn)
Defensive patterns

Strategy: validation

Validate before calling

// build a DSN that avoids the public-key request path
func mysqlDSN(user, pass, host, db string) string {
    // tls=true makes caching_sha2_password use cleartext over TLS
    return fmt.Sprintf("%s:%s@tcp(%s)/%s?tls=true", user, pass, host, db)
}
// or use net=unix to take the cleartext fast path

Try / catch

db, err := sql.Open("mysql", dsn)
if err := db.PingContext(ctx); err != nil {
    if strings.Contains(err.Error(), "unexpected resp from server for caching_sha2_password") {
        // switch DSN to tls=true / unix socket and retry
    }
}

Prevention

When it happens

Trigger: Connecting with caching_sha2_password (MySQL 8 default) over a non-TLS, non-unix-socket connection WITHOUT a preconfigured server public key, where the server's response to the public-key request is not a key blob — e.g. the server replied with an error or terminated auth.

Common situations: MySQL 8+ over plain TCP with no TLS and no allowOldPasswords/serverPubKey configured; an intermediary (proxy, RDS proxy) that rewrites the auth handshake; a server that rejects the account mid-handshake; version/compatibility skew between client and server auth plugins.

Related errors


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