golang/go · error
tls: internal error: empty verified chain
Error message
tls: internal error: empty verified chain
What it means
Thrown during serialization of a TLS SessionState (ticket.go:151) when one of the entries in s.verifiedChains has length zero. The marshaler elides the leaf cert and writes chain[1:], so an empty chain is structurally invalid. This is a defensive/internal error: a well-formed SessionState produced by the TLS stack always has at least the leaf in each verified chain.
Source
Thrown at src/crypto/tls/ticket.go:151
} else {
b.AddUint8(0)
}
if s.EarlyData {
b.AddUint8(1)
} else {
b.AddUint8(0)
}
marshalCertificate(&b, Certificate{
Certificate: certificatesToBytesSlice(s.peerCertificates),
OCSPStaple: s.ocspResponse,
SignedCertificateTimestamps: s.scts,
})
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
for _, chain := range s.verifiedChains {
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
// We elide the first certificate because it's always the leaf.
if len(chain) == 0 {
b.SetError(errors.New("tls: internal error: empty verified chain"))
return
}
for _, cert := range chain[1:] {
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(cert.Raw)
})
}
})
}
})
if s.EarlyData {
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes([]byte(s.alpnProtocol))
})
}
if s.version >= VersionTLS13 {
if s.isClient {
addUint64(&b, s.useBy)View on GitHub (pinned to b6b368adc5)
Solutions
- Do not hand-build SessionState.verifiedChains; obtain SessionState from the stack via ClientSessionState/ServerSessionState and round-trip it with Bytes()/ParseSessionState().
- If you must construct it, ensure every entry in verifiedChains is non-empty and starts with the leaf certificate (the marshaler elides chain[0]).
- Validate verifiedChains before serializing: for each chain check len(chain) > 0 and chain[0] != nil.
- If the state came from a cache, treat corruption as a cache miss: drop the ticket and force a full handshake.
Example fix
// before
ss := &tls.SessionState{
verifiedChains: [][]*x509.Certificate{{}}, // empty chain -> error
}
data, err := ss.Bytes()
// after
ss := &tls.SessionState{
verifiedChains: [][]*x509.Certificate{{leafCert, intermediateCert}},
}
data, err := ss.Bytes() Defensive patterns
Strategy: validation
Validate before calling
// Before calling ss.Bytes(), ensure every verified chain is non-empty.
func validVerifiedChains(ss *tls.SessionState) bool {
for _, chain := range ss.verifiedChains {
if len(chain) == 0 {
return false
}
for _, c := range chain {
if c == nil || len(c.Raw) == 0 {
return false
}
}
}
return true
} Prevention
- Never hand-construct SessionState.verifiedChains; obtain it from the TLS stack.
- When round-tripping through a cache, prefer tls.ParseSessionState on retrieval and discard on error.
- Treat this 'internal error' as cache corruption — drop the ticket and force a full handshake.
When it happens
Trigger: Calling (*SessionState).Bytes() (via SessionState.Bytes / session ticket marshaling) on a SessionState whose verifiedChains slice contains an empty []string/[]*x509.Certificate entry. Most often reached when application code hand-constructs a SessionState and appends an empty chain, or when a custom ClientSessionCache/ServerSessionCache stores a corrupted state.
Common situations: Manually building a SessionState for testing or for a custom ticket key rotation scheme; downgrading/transforming session data between Go versions; bugs in resumption code that mutate verifiedChains; importing session blobs from non-Go TLS implementations that omit chain construction.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: internal error: supportsCurve accepted unimplemented cu
- tls: server selected an invalid PSK
- tls: server selected an invalid PSK and cipher suite pair
- tls: session supported extended_master_secret but client doe
- tls: internal error: unknown cipher suite
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/ace08fbe529f89c5.
Report an issue: GitHub.