gravitational/teleport · error · ConnectionProblem

failed to fetch MySQL version

Error message

failed to fetch MySQL version

What it means

'failed to fetch MySQL version' is raised by readHandshakeError (called from ReadMySQLVersion) in lib/srv/db/mysql/protocol/version.go. When probing a MySQL server's version, Teleport receives an error packet instead of a handshake packet; the code wraps the raw MySQL error packet inside a trace.ConnectionProblem with this message.

Source

Thrown at lib/srv/db/mysql/protocol/version.go:106

	versionLength := bytes.IndexByte(handshake[1:], 0x00)
	if versionLength == -1 {
		return "", trace.Errorf("failed to read the MySQL server version")
	}

	return string(handshake[1 : 1+versionLength]), nil
}

// readHandshakeError reads and returns an error message from
func readHandshakeError(connBuf io.Reader) (string, error) {
	handshakePacket, err := ParsePacket(connBuf)
	if err != nil {
		return "", err
	}
	errPackage, ok := handshakePacket.(*Error)
	if !ok {
		return "", trace.BadParameter("expected MySQL error package, got %T", handshakePacket)
	}
	return "", trace.ConnectionProblem(errors.New("failed to fetch MySQL version"), "%s", errPackage.Error())
}

// IsHandshakeV10Packet peeks into the conn and checks for a handshake v10 packet.
// The results of this function are only meaningful during the connection phase
// of the MySQL protocol. It is the caller's responsibility to only use this
// function during the connection phase.
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_v10.html
func IsHandshakeV10Packet(conn BufferedConn) (bool, error) {
	pkgHeaderAndType, err := conn.Peek(packetHeaderAndTypeSize)
	if err != nil {
		return false, trace.Wrap(err)
	}
	const typeIdx = packetHeaderAndTypeSize - 1
	return pkgHeaderAndType[typeIdx] == 10, nil
}

// BufferedConn is a net.Conn wrapper with additional Peek() method.
type BufferedConn struct {

View on GitHub (pinned to 1283425b60)

Solutions

  1. Run 'mysqladmin flush-hosts' or restart MySQL if the host is blocked due to connection errors
  2. Check the server error log for the underlying MySQL error packet text
  3. Raise max_connections or fix connection leaks if the server is out of connections
  4. Verify you are pointing at the correct MySQL port and that the probe's TLS settings match the server requirements
Defensive patterns

Strategy: type-guard

Validate before calling

// probe reachability and avoid hammering the server into a blocked state
if err := tcpProbe(mysqlHost, mysqlPort); err != nil {
  return fmt.Errorf("mysql unreachable before version probe: %w", err)
}

Type guard

func isMySQLVersionFetchError(err error) bool {
  return trace.IsConnectionProblem(err) && strings.Contains(err.Error(), "failed to fetch MySQL version")
}

Try / catch

ver, err := protocol.ReadMySQLVersion(ctx, conn)
if err != nil {
  if isMySQLVersionFetchError(err) {
    log.Printf("mysql refused version probe: %v", trace.Unwrap(err))
    return mysqlDefaultVersion, nil // or surface the server's error text
  }
  return err
}

Prevention

When it happens

Trigger: ReadMySQLVersion connects to a MySQL server and the first packet received is *protocol.Error rather than a HandshakeV10 — e.g. the server rejects the pre-auth probe due to blocked host, too many connections, or auth/SSL requirements.

Common situations: MySQL server reports 'Host ... is blocked because of many connection errors', max_connections exceeded, or connecting to something that is not a standard MySQL endpoint (e.g. a proxy or a TLS-only port).

Related errors


AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02). Data as JSON: /api/errors/83945b5f86e9e538. Report an issue: GitHub.