golang/go · error
tls: server did not echo the legacy session ID
Error message
tls: server did not echo the legacy session ID
What it means
Thrown in checkServerHelloOrHRR() when the server's TLS 1.3 ServerHello does not echo back the client's legacy session ID. RFC 8446 section 4.1.3 requires the server to echo the client's session ID for middlebox compatibility — a non-matching session ID is a protocol violation.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:196
if hs.serverHello.vers != VersionTLS12 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server sent an incorrect legacy version")
}
if hs.serverHello.ocspStapling ||
hs.serverHello.ticketSupported ||
hs.serverHello.extendedMasterSecret ||
hs.serverHello.secureRenegotiationSupported ||
len(hs.serverHello.secureRenegotiation) != 0 ||
len(hs.serverHello.alpnProtocol) != 0 ||
len(hs.serverHello.scts) != 0 {
c.sendAlert(alertUnsupportedExtension)
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
}
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server did not echo the legacy session ID")
}
if hs.serverHello.compressionMethod != compressionNone {
c.sendAlert(alertDecodeError)
return errors.New("tls: server sent non-zero legacy TLS compression method")
}
selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
if hs.suite != nil && selectedSuite != hs.suite {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
}
if selectedSuite == nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server chose an unconfigured cipher suite")
}
hs.suite = selectedSuite
c.cipherSuite = hs.suite.idView on GitHub (pinned to b6b368adc5)
Solutions
- Server must echo the client's legacy session ID verbatim in the TLS 1.3 ServerHello per RFC 8446 section 4.1.3.
- Check for middlebox or proxy interference modifying the session ID field.
- If the server cannot be fixed, restrict the client to TLS 1.2.
- Report as a server compliance issue with a packet capture showing the mismatched session IDs.
Defensive patterns
Strategy: try-catch
Try / catch
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
if strings.Contains(err.Error(), "did not echo the legacy session ID") {
// Server protocol violation — fall back to TLS 1.2
config.MaxVersion = tls.VersionTLS12
conn, err = tls.Dial("tcp", addr, config)
}
} Prevention
- Capture handshakes with Wireshark to verify session ID echoing.
- Report session ID echo violations to server vendors.
- Keep a TLS 1.2 fallback for servers with broken middlebox compatibility.
When it happens
Trigger: Triggered when bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) returns false. The client sent a non-empty legacy session ID but the server returned a different value or omitted it.
Common situations: Non-compliant server that generates a new session ID instead of echoing the client's. TLS middlebox or proxy modifying the session ID field. Server implementation that doesn't follow the middlebox compatibility requirement. Server that treats session ID differently in TLS 1.3.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server selected TLS 1.3 using the legacy version field
- tls: server selected an invalid version after a HelloRetryRe
- tls: server sent an incorrect legacy version
- tls: server sent a ServerHello extension forbidden in TLS 1.
- tls: server sent an unnecessary HelloRetryRequest message
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/1c9176a697dc4d02.
Report an issue: GitHub.