golang/go · error

tls: invalid NextProtos value

Error message

tls: invalid NextProtos value

What it means

Thrown by makeClientHello when any entry in config.NextProtos (the ALPN protocol ID list) has a length of 0 or exceeds 255 bytes. ALPN protocol IDs are encoded with a single-byte length prefix on the wire, so each must be between 1 and 255 bytes long. An empty string or an excessively long protocol name violates this constraint.

Solutions

  1. Remove empty strings from config.NextProtos before use
  2. Ensure each ALPN protocol ID is between 1 and 255 bytes long
  3. Filter the NextProtos list programmatically: slices.DeleteFunc(nextProtos, func(s string) bool { return len(s) == 0 || len(s) > 255 })
  4. Validate ALPN entries from external input (config files, user input) before assigning to NextProtos

Example fix

// before
config := &tls.Config{
    NextProtos: []string{"h2", "", "http/1.1"}, // empty string → error
}
// after
config := &tls.Config{
    NextProtos: []string{"h2", "http/1.1"},
}
Defensive patterns

Strategy: validation

Validate before calling

func validateNextProtos(protos []string) error {
    for _, p := range protos {
        if l := len(p); l == 0 || l > 255 {
            return fmt.Errorf("invalid ALPN protocol length %d: must be 1-255 bytes", l)
        }
    }
    return nil
}

// Usage:
//   if err := validateNextProtos(config.NextProtos); err != nil {
//       log.Fatal(err)
//   }

Try / catch

// Pre-validate before assigning to config:
//
//   protos := slices.DeleteFunc(rawProtos, func(s string) bool {
//       return len(s) == 0 || len(s) > 255
//   })
//   config.NextProtos = protos

Prevention

When it happens

Trigger: Setting config.NextProtos to a slice containing an empty string (e.g., []string{"h2", ""}) or a string longer than 255 bytes (e.g., []string{strings.Repeat("a", 300)}).

Common situations: Accidentally including an empty string from a comma-split or whitespace-trim operation. Programmatically generated protocol names that exceed 255 bytes. A configuration file with a trailing comma producing an empty entry. Accidental nil interface conversion producing an empty string.

Understand the failure class

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/fdf1eb955f84556d. Report an issue: GitHub.

Appendix: source

Thrown at src/crypto/tls/handshake_client.go:53

	serverHello  *serverHelloMsg
	hello        *clientHelloMsg
	suite        *cipherSuite
	finishedHash finishedHash
	masterSecret []byte
	session      *SessionState // the session being resumed
	ticket       []byte        // a fresh ticket received during this handshake
}

func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
	config := c.config
	if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
		return nil, nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
	}

	nextProtosLength := 0
	for _, proto := range config.NextProtos {
		if l := len(proto); l == 0 || l > 255 {
			return nil, nil, nil, errors.New("tls: invalid NextProtos value")
		} else {
			nextProtosLength += 1 + l
		}
	}
	if nextProtosLength > 0xffff {
		return nil, nil, nil, errors.New("tls: NextProtos values too large")
	}

	supportedVersions := config.supportedVersions(roleClient, c.quic != nil)
	if len(supportedVersions) == 0 {
		return nil, nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
	}
	// Since supportedVersions is sorted in descending order, the first element
	// is the maximum version and the last element is the minimum version.
	maxVersion := supportedVersions[0]
	minVersion := supportedVersions[len(supportedVersions)-1]

	hello := &clientHelloMsg{

View on GitHub (pinned to b6b368adc5)