{"record":{"id":"7851351b8a668463","repo":"golang/go","slug":"tls-server-did-not-select-an-alpn-protocol","errorCode":null,"errorMessage":"tls: server did not select an ALPN protocol","messagePattern":"tls: server did not select an ALPN protocol","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_client.go","lineNumber":974,"sourceCode":"\tc.verifiedChains = hs.session.verifiedChains\n\tc.ocspResponse = hs.session.ocspResponse\n\t// Let the ServerHello SCTs override the session SCTs from the original\n\t// connection, if any are provided.\n\tif len(c.scts) == 0 && len(hs.session.scts) != 0 {\n\t\tc.scts = hs.session.scts\n\t}\n\tc.curveID = hs.session.curveID\n\n\treturn true, nil\n}\n\n// checkALPN ensure that the server's choice of ALPN protocol is compatible with\n// the protocols that we advertised in the ClientHello.\nfunc checkALPN(clientProtos []string, serverProto string, quic bool) error {\n\tif serverProto == \"\" {\n\t\tif quic && len(clientProtos) > 0 {\n\t\t\t// RFC 9001, Section 8.1\n\t\t\treturn errors.New(\"tls: server did not select an ALPN protocol\")\n\t\t}\n\t\treturn nil\n\t}\n\tif len(clientProtos) == 0 {\n\t\treturn errors.New(\"tls: server advertised unrequested ALPN extension\")\n\t}\n\tfor _, proto := range clientProtos {\n\t\tif proto == serverProto {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn errors.New(\"tls: server selected unadvertised ALPN protocol\")\n}\n\nfunc (hs *clientHandshakeState) readFinished(out []byte) error {\n\tc := hs.c\n\n\tif err := c.readChangeCipherSpec(); err != nil {","sourceCodeStart":956,"sourceCodeEnd":992,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_client.go#L956-L992","documentation":"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.","triggerScenarios":"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.","commonSituations":"QUIC server without ALPN configured; offering NextProtos that the server cannot match; using a QUIC library whose Config.NextProtos is empty or wrong.","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."],"exampleFix":"// before: missing ALPN for QUIC\ncfg := &tls.Config{}\n// after: offer h3 so the QUIC server can select\n// (NextProtos is set by the QUIC library; shown for clarity)\ncfg := &tls.Config{NextProtos: []string{\"h3\"}}","handlingStrategy":"validation","validationCode":"// For QUIC, ensure Config.NextProtos is non-empty before handing the config to the QUIC stack.\nfunc validateQUICConfig(cfg *tls.Config) error {\n    if len(cfg.NextProtos) == 0 {\n        return errors.New(\"QUIC requires at least one ALPN protocol in Config.NextProtos\")\n    }\n    return nil\n}","typeGuard":"func isQUICNoALPN(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"server did not select an ALPN protocol\")\n}","tryCatchPattern":"if err := validateQUICConfig(cfg); err != nil { return err }\n// (Then perform the QUIC handshake; on isQUICNoALPN, fix server-side ALPN config.)","preventionTips":["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."],"tags":["tls","quic","alpn","protocol"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}