go-sql-driver/mysql · critical

no pem data found, data: %s

Error message

no pem data found, data: %s

What it means

Thrown in the caching_sha2_password full-auth path (auth.go:432) after the server's public-key response IS received (it passed the iAuthMoreData check) but pem.Decode could not parse a PEM block from it. The %s shows the leftover bytes that pem.Decode could not consume — indicating the payload is not a valid PEM-encoded RSA public key.

Source

Thrown at auth.go:432

						}
						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
					}
				}
				return mc.resultUnchanged().readResultOK()

			default:
				return ErrMalformPkt

View on GitHub (pinned to c426bd9379)

Solutions

  1. Enable TLS (tls=true) or use a unix socket to avoid the public-key exchange entirely.
  2. Inspect the %s bytes in the error — if they look like an error message, the server is rejecting auth for another reason (fix the underlying auth issue).
  3. Bypass proxies/routers; connect directly to MySQL to rule out payload mangling.
  4. If you can pin the server key, register it as the DSN server public key to skip the exchange.

Example fix

// before: caching_sha2_password over plain TCP
// dsn := "user:pass@tcp(host:3306)/db"

// after: TLS avoids requesting/parsing the server public key
dsn := "user:pass@tcp(host:3306)/db?tls=skip-verify"
db, err := sql.Open("mysql", dsn)
Defensive patterns

Strategy: validation

Validate before calling

// prefer TLS/unix so the server key is never requested/parsed
func safeDSN(user, pass, host, db string) string {
    return fmt.Sprintf("%s:%s@tcp(%s)/%s?tls=true", user, pass, host, db)
}

Try / catch

if err := db.PingContext(ctx); err != nil {
    if strings.Contains(err.Error(), "no pem data found") {
        // server's key payload was unparseable; switch to TLS/unix and retry
    }
}

Prevention

When it happens

Trigger: Connecting with caching_sha2_password over plain TCP without TLS, where the server DID respond to the public-key request but the payload is not PEM-encoded PKIX RSA key data — corrupted bytes, a proxy rewriting the response, or a non-conforming server.

Common situations: A proxy/router that mangles the auth-more-data packet; packet corruption on a flaky link; a MySQL fork that sends the key in a non-PEM format; a man-in-the-middle injecting garbage. The trailing %s in the message is the diagnostic clue.

Related errors


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