{"record":{"id":"1060f321cbd02dbd","repo":"golang/go","slug":"tls-maxversion-must-be-versiontls13-if-encrypt","errorCode":null,"errorMessage":"tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated","messagePattern":"tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_client.go","lineNumber":180,"sourceCode":"\n\tif c.quic != nil {\n\t\tp, err := c.quicGetTransportParameters()\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\t\tif p == nil {\n\t\t\tp = []byte{}\n\t\t}\n\t\thello.quicTransportParameters = p\n\t}\n\n\tvar ech *echClientContext\n\tif c.config.EncryptedClientHelloConfigList != nil {\n\t\tif c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 {\n\t\t\treturn nil, nil, nil, errors.New(\"tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated\")\n\t\t}\n\t\tif c.config.MaxVersion != 0 && c.config.MaxVersion <= VersionTLS12 {\n\t\t\treturn nil, nil, nil, errors.New(\"tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated\")\n\t\t}\n\t\techConfigs, err := parseECHConfigList(c.config.EncryptedClientHelloConfigList)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\t\techConfig, echPK, kdf, aead := pickECHConfig(echConfigs)\n\t\tif echConfig == nil {\n\t\t\treturn nil, nil, nil, errors.New(\"tls: EncryptedClientHelloConfigList contains no valid configs\")\n\t\t}\n\t\tech = &echClientContext{config: echConfig, kdfID: kdf.ID(), aeadID: aead.ID()}\n\t\thello.encryptedClientHello = []byte{1} // indicate inner hello\n\t\t// We need to explicitly set these 1.2 fields to nil, as we do not\n\t\t// marshal them when encoding the inner hello, otherwise transcripts\n\t\t// will later mismatch.\n\t\thello.supportedPoints = nil\n\t\thello.ticketSupported = false\n\t\thello.secureRenegotiationSupported = false\n\t\thello.extendedMasterSecret = false","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_client.go#L162-L198","documentation":"Thrown by makeClientHello when config.EncryptedClientHelloConfigList is non-nil (ECH is enabled on the client) AND config.MaxVersion is explicitly set to VersionTLS12 (0x0303) or below. ECH requires TLS 1.3+, so capping the maximum version at 1.2 makes ECH impossible. This check fires when MaxVersion != 0 and MaxVersion <= VersionTLS12.","triggerScenarios":"Setting both EncryptedClientHelloConfigList (to a non-nil byte slice) and MaxVersion to VersionTLS12 (0x0303), VersionTLS11 (0x0302), or VersionTLS10 (0x0301) simultaneously.","commonSituations":"Configuring ECH while keeping MaxVersion=VersionTLS12 for legacy server compatibility. Adding ECH to a config designed for TLS 1.2-only environments. Copying a config with a TLS 1.2 ceiling and enabling ECH without raising MaxVersion.","solutions":["Set MaxVersion to tls.VersionTLS13 (or leave it as 0 for the default maximum, which includes TLS 1.3)","Remove the MaxVersion cap entirely if ECH is required — ECH cannot work with a TLS 1.2 ceiling","If you genuinely need TLS 1.2 as the maximum version, do not enable ECH — they are incompatible"],"exampleFix":"// before\nconfig := &tls.Config{\n    EncryptedClientHelloConfigList: echConfigList,\n    MaxVersion:                    tls.VersionTLS12,\n}\n// after\nconfig := &tls.Config{\n    EncryptedClientHelloConfigList: echConfigList,\n    MaxVersion:                    tls.VersionTLS13,\n}","handlingStrategy":"validation","validationCode":"func validateECHClientConfig(config *tls.Config) error {\n    if config.EncryptedClientHelloConfigList == nil {\n        return nil\n    }\n    if config.MinVersion != 0 && config.MinVersion < tls.VersionTLS13 {\n        return errors.New(\"MinVersion must be >= VersionTLS13 when ECH is enabled\")\n    }\n    if config.MaxVersion != 0 && config.MaxVersion <= tls.VersionTLS12 {\n        return errors.New(\"MaxVersion must be >= VersionTLS13 when ECH is enabled\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// Pre-validate before dial:\n//\n//   if err := validateECHClientConfig(config); err != nil {\n//       config.MaxVersion = 0 // reset to default (allows TLS 1.3)\n//   }","preventionTips":["Leave MaxVersion unset (0) or set it to tls.VersionTLS13 when using ECH","Never set MaxVersion to VersionTLS12 or below when ECH is enabled","Validate the full ECH + version configuration in a constructor function"],"tags":["tls","client-side","config","ech","version-negotiation","tls13"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}