golang/go · error

tls: NextProtos values too large

Error message

tls: NextProtos values too large

What it means

Thrown by makeClientHello when the total serialized size of all config.NextProtos entries (including 1-byte length prefix per entry) exceeds 65535 bytes (0xFFFF). The ALPN extension encodes the protocol list with a uint16 overall length prefix, capping the total at 64KB. This is unlikely with normal usage but possible with many or very large protocol IDs.

Solutions

  1. Reduce the number of entries or the length of individual entries in config.NextProtos
  2. Remove unnecessary or duplicate ALPN protocol IDs — typically only 2-3 are needed (e.g., "h2", "http/1.1")
  3. Validate total size before assignment: ensure sum of (1 + len(proto)) for all entries is at most 65535
  4. Cap external input that feeds into NextProtos to a reasonable maximum

Example fix

// before — overly large protocol list
config := &tls.Config{
    NextProtos: generateLargeProtocolList(), // total > 64KB
}
// after — trim to essential protocols
config := &tls.Config{
    NextProtos: []string{"h2", "http/1.1"},
}
Defensive patterns

Strategy: validation

Validate before calling

func validateNextProtosSize(protos []string) error {
    total := 0
    for _, p := range protos {
        total += 1 + len(p)
    }
    if total > 0xffff {
        return fmt.Errorf("total NextProtos size %d exceeds 65535 bytes", total)
    }
    return nil
}

Try / catch

// Pre-validate total size:
//
//   if err := validateNextProtosSize(config.NextProtos); err != nil {
//       config.NextProtos = config.NextProtos[:2] // trim to essential protocols
//   }

Prevention

When it happens

Trigger: Setting config.NextProtos to a slice where the sum of (1 + len(proto)) for all entries exceeds 0xFFFF (65535).

Common situations: Accidentally duplicating entries many times in a loop. Including extremely long protocol ID strings. A programming error that appends a large data structure as protocol names. Generating NextProtos from unbounded external input without size limits.

Understand the failure class

Related errors


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

Appendix: source

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

	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{
		vers:                         maxVersion,
		compressionMethods:           []uint8{compressionNone},
		random:                       make([]byte, 32),
		extendedMasterSecret:         true,
		ocspStapling:                 true,
		scts:                         true,

View on GitHub (pinned to b6b368adc5)