golang/go · error
tls: server selected unadvertised ALPN protocol
Error message
tls: server selected unadvertised ALPN protocol
What it means
In checkALPN, after confirming the client offered at least one protocol, the loop checks each client proto against the server's selection. If none match, the server selected a protocol the client never advertised, which violates the ALPN RFC (7301) — the server must pick from the client's offer.
Source
Thrown at src/crypto/tls/handshake_client.go:986
// 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
// sent is used during verification.
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
serverFinished, ok := msg.(*finishedMsg)
if !ok {View on GitHub (pinned to b6b368adc5)
Solutions
- Add the protocol the server will select to Config.NextProtos (e.g. include "http/1.1" alongside "h2").
- Align the server's ALPN configuration with the protocols clients offer.
- Verify no intermediary is overriding the server's selection.
Example fix
// before: only offering h2, server picks http/1.1
cfg := &tls.Config{NextProtos: []string{"h2"}}
// after: offer both so the server can select either
cfg := &tls.Config{NextProtos: []string{"h2", "http/1.1"}} Defensive patterns
Strategy: validation
Validate before calling
// Ensure the protocol the server will select is in your offer list.
func validateALPN(offer, serverWillSelect string) error {
for _, p := range strings.Split(offer, ",") {
if p == serverWillSelect { return nil }
}
return fmt.Errorf("server will select %q which is not in NextProtos", serverWillSelect)
} Type guard
func isUnadvertisedALPN(err error) bool {
return err != nil && strings.Contains(err.Error(), "server selected unadvertised ALPN protocol")
} Try / catch
if _, err := tls.Dial("tcp", addr, cfg); err != nil {
if isUnadvertisedALPN(err) {
// Broaden the offer to include what the server selects.
cfg.NextProtos = []string{"h2", "http/1.1"}
_, err = tls.Dial("tcp", addr, cfg)
}
} Prevention
- Include all acceptable protocols in Config.NextProtos (e.g. h2 and http/1.1).
- Align client and server ALPN configuration.
- Watch for ingress overriding server ALPN selection.
When it happens
Trigger: Client offers NextProtos = ["h2"] but the server returns "http/1.1"; mismatched ALPN configuration between client and server; ingress/load balancer selecting a different protocol than the client offered.
Common situations: NextProtos list not matching what the server expects; server misconfigured to always select a fixed protocol; load balancer overriding ALPN.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: invalid NextProtos value
- tls: NextProtos values too large
- tls: server did not select an ALPN protocol
- tls: server advertised unrequested ALPN extension
- tls: either ServerName or InsecureSkipVerify must be specifi
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/9b13479aa7a097a5.
Report an issue: GitHub.