{"record":{"id":"9b13479aa7a097a5","repo":"golang/go","slug":"tls-server-selected-unadvertised-alpn-protocol","errorCode":null,"errorMessage":"tls: server selected unadvertised ALPN protocol","messagePattern":"tls: server selected unadvertised ALPN protocol","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_client.go","lineNumber":986,"sourceCode":"// 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 {\n\t\treturn err\n\t}\n\n\t// finishedMsg is included in the transcript, but not until after we\n\t// check the client version, since the state before this message was\n\t// sent is used during verification.\n\tmsg, err := c.readHandshake(nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tserverFinished, ok := msg.(*finishedMsg)\n\tif !ok {","sourceCodeStart":968,"sourceCodeEnd":1004,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_client.go#L968-L1004","documentation":"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.","triggerScenarios":"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.","commonSituations":"NextProtos list not matching what the server expects; server misconfigured to always select a fixed protocol; load balancer overriding ALPN.","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."],"exampleFix":"// before: only offering h2, server picks http/1.1\ncfg := &tls.Config{NextProtos: []string{\"h2\"}}\n// after: offer both so the server can select either\ncfg := &tls.Config{NextProtos: []string{\"h2\", \"http/1.1\"}}","handlingStrategy":"validation","validationCode":"// Ensure the protocol the server will select is in your offer list.\nfunc validateALPN(offer, serverWillSelect string) error {\n    for _, p := range strings.Split(offer, \",\") {\n        if p == serverWillSelect { return nil }\n    }\n    return fmt.Errorf(\"server will select %q which is not in NextProtos\", serverWillSelect)\n}","typeGuard":"func isUnadvertisedALPN(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"server selected unadvertised ALPN protocol\")\n}","tryCatchPattern":"if _, err := tls.Dial(\"tcp\", addr, cfg); err != nil {\n    if isUnadvertisedALPN(err) {\n        // Broaden the offer to include what the server selects.\n        cfg.NextProtos = []string{\"h2\", \"http/1.1\"}\n        _, err = tls.Dial(\"tcp\", addr, cfg)\n    }\n}","preventionTips":["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."],"tags":["tls","alpn","config","protocol"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}