golang/go · error

tls: server advertised unrequested ALPN extension

Error message

tls: server advertised unrequested ALPN extension

What it means

In checkALPN, if the server returned a non-empty ALPN value (serverProto != "") but the client sent no ALPN extension (len(clientProtos) == 0), the server invented an extension the client never offered — a protocol violation.

Source

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

		c.scts = hs.session.scts
	}
	c.curveID = hs.session.curveID

	return true, nil
}

// checkALPN ensure that the server's choice of ALPN protocol is compatible with
// the protocols that we advertised in the ClientHello.
func checkALPN(clientProtos []string, serverProto string, quic bool) error {
	if serverProto == "" {
		if quic && len(clientProtos) > 0 {
			// RFC 9001, Section 8.1
			return errors.New("tls: server did not select an ALPN protocol")
		}
		return nil
	}
	if len(clientProtos) == 0 {
		return errors.New("tls: server advertised unrequested ALPN extension")
	}
	for _, proto := range clientProtos {
		if proto == serverProto {
			return nil
		}
	}
	return errors.New("tls: server selected unadvertised ALPN protocol")
}

func (hs *clientHandshakeState) readFinished(out []byte) error {
	c := hs.c

	if err := c.readChangeCipherSpec(); err != nil {
		return err
	}

	// finishedMsg is included in the transcript, but not until after we
	// check the client version, since the state before this message was

View on GitHub (pinned to b6b368adc5)

Solutions

  1. If you want ALPN, set Config.NextProtos to the protocols you will accept.
  2. Patch the server so it does not return ALPN when the client did not offer it.
  3. Investigate the path for an injecting middlebox.

Example fix

// If you expect ALPN, advertise what you accept:
cfg := &tls.Config{NextProtos: []string{"h2", "http/1.1"}}
Defensive patterns

Strategy: validation

Validate before calling

// Decide intentionally whether you want ALPN, then set NextProtos accordingly.
// If the server returns ALPN when you offered none, the fix is either to offer ALPN
// or to make the server stop sending it.
func wantsALPN(yes bool, cfg *tls.Config) {
    if yes { cfg.NextProtos = []string{"h2", "http/1.1"} } else { cfg.NextProtos = nil }
}

Type guard

func isUnrequestedALPN(err error) bool {
    return err != nil && strings.Contains(err.Error(), "server advertised unrequested ALPN extension")
}

Try / catch

if _, err := tls.Dial("tcp", addr, cfg); err != nil {
    if isUnrequestedALPN(err) {
        // Server bug; either accept ALPN by advertising it or report the defect.
        cfg.NextProtos = []string{"http/1.1"}
        _, err = tls.Dial("tcp", addr, cfg)
    }
}

Prevention

When it happens

Trigger: Server sending an ALPN selection while the client offered no ALPN; middlebox injecting ALPN; buggy server with an unconditional ALPN response.

Common situations: Server with aggressive ALPN defaults; intermediary that adds ALPN; rare.

Understand the failure class

Related errors


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