golang/go · error
tls: server did not select an ALPN protocol
Error message
tls: server did not select an ALPN protocol
What it means
In checkALPN, when quic == true (the connection is over QUIC) and the client advertised at least one ALPN protocol (len(clientProtos) > 0) but the server returned an empty selection (serverProto == ""), RFC 9001 8.1 makes this a connection error. For TCP TLS an empty selection is allowed; QUIC requires the server to pick one.
Source
Thrown at src/crypto/tls/handshake_client.go:974
c.verifiedChains = hs.session.verifiedChains
c.ocspResponse = hs.session.ocspResponse
// Let the ServerHello SCTs override the session SCTs from the original
// connection, if any are provided.
if len(c.scts) == 0 && len(hs.session.scts) != 0 {
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 {View on GitHub (pinned to b6b368adc5)
Solutions
- Ensure Config.NextProtos is non-empty and contains the protocol(s) the QUIC server will select (typically "h3" for HTTP/3).
- Configure the server to select one of the offered ALPN values.
- Verify you are passing the QUIC-mode Config through the QUIC stack correctly.
Example fix
// before: missing ALPN for QUIC
cfg := &tls.Config{}
// after: offer h3 so the QUIC server can select
// (NextProtos is set by the QUIC library; shown for clarity)
cfg := &tls.Config{NextProtos: []string{"h3"}} Defensive patterns
Strategy: validation
Validate before calling
// For QUIC, ensure Config.NextProtos is non-empty before handing the config to the QUIC stack.
func validateQUICConfig(cfg *tls.Config) error {
if len(cfg.NextProtos) == 0 {
return errors.New("QUIC requires at least one ALPN protocol in Config.NextProtos")
}
return nil
} Type guard
func isQUICNoALPN(err error) bool {
return err != nil && strings.Contains(err.Error(), "server did not select an ALPN protocol")
} Try / catch
if err := validateQUICConfig(cfg); err != nil { return err }
// (Then perform the QUIC handshake; on isQUICNoALPN, fix server-side ALPN config.) Prevention
- Always set Config.NextProtos for QUIC (typically "h3").
- Ensure the server selects from the client's offered ALPN.
- Pass the Config through the QUIC stack so quic=true is set.
When it happens
Trigger: QUIC handshake via crypto/tls's QUIC mode (Config used with quic:true, e.g. through a QUIC library) where the server did not return an ALPN value; client offered only h3 but the server only knows http/1.1; QUIC server misconfiguration.
Common situations: QUIC server without ALPN configured; offering NextProtos that the server cannot match; using a QUIC library whose Config.NextProtos is empty or wrong.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server advertised unrequested ALPN extension
- tls: server selected unadvertised ALPN protocol
- tls: invalid NextProtos value
- tls: NextProtos values too large
- tls: server echoed TLS 1.3 compatibility session ID in TLS 1
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/7851351b8a668463.
Report an issue: GitHub.