{"record":{"id":"2180faf95362a078","repo":"golang/go","slug":"tls-server-advertised-unrequested-alpn-extension","errorCode":null,"errorMessage":"tls: server advertised unrequested ALPN extension","messagePattern":"tls: server advertised unrequested ALPN extension","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_client.go","lineNumber":979,"sourceCode":"\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 {\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","sourceCodeStart":961,"sourceCodeEnd":997,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_client.go#L961-L997","documentation":"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.","triggerScenarios":"Server sending an ALPN selection while the client offered no ALPN; middlebox injecting ALPN; buggy server with an unconditional ALPN response.","commonSituations":"Server with aggressive ALPN defaults; intermediary that adds ALPN; rare.","solutions":["If you want ALPN, set Config.NextProtos to the protocols you will accept.","Patch the server so it does not return ALPN when the client did not offer it.","Investigate the path for an injecting middlebox."],"exampleFix":"// If you expect ALPN, advertise what you accept:\ncfg := &tls.Config{NextProtos: []string{\"h2\", \"http/1.1\"}}","handlingStrategy":"validation","validationCode":"// Decide intentionally whether you want ALPN, then set NextProtos accordingly.\n// If the server returns ALPN when you offered none, the fix is either to offer ALPN\n// or to make the server stop sending it.\nfunc wantsALPN(yes bool, cfg *tls.Config) {\n    if yes { cfg.NextProtos = []string{\"h2\", \"http/1.1\"} } else { cfg.NextProtos = nil }\n}","typeGuard":"func isUnrequestedALPN(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"server advertised unrequested ALPN extension\")\n}","tryCatchPattern":"if _, err := tls.Dial(\"tcp\", addr, cfg); err != nil {\n    if isUnrequestedALPN(err) {\n        // Server bug; either accept ALPN by advertising it or report the defect.\n        cfg.NextProtos = []string{\"http/1.1\"}\n        _, err = tls.Dial(\"tcp\", addr, cfg)\n    }\n}","preventionTips":["Set Config.NextProtos when you expect ALPN.","Report servers that send ALPN unsolicited."],"tags":["tls","alpn","protocol"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}