golang/go · error

tls: invalid session encoding

Error message

tls: invalid session encoding

What it means

Returned by ParseSessionState (ticket.go:203) when the broad structural read of the session blob fails. The chained condition reads version, type byte, cipher suite, creation time, secret, extra blob, extMasterSecret flag, earlyData flag, and the certificate, asserting the secret is non-empty. If ANY of those reads fails or the secret is empty, the blob is considered corrupt or truncated and this error fires.

Source

Thrown at src/crypto/tls/ticket.go:203

// ParseSessionState parses a [SessionState] encoded by [SessionState.Bytes].
func ParseSessionState(data []byte) (*SessionState, error) {
	ss := &SessionState{}
	s := cryptobyte.String(data)
	var typ, extMasterSecret, earlyData uint8
	var cert Certificate
	var extra cryptobyte.String
	if !s.ReadUint16(&ss.version) ||
		!s.ReadUint8(&typ) ||
		!s.ReadUint16(&ss.cipherSuite) ||
		!readUint64(&s, &ss.createdAt) ||
		!readUint8LengthPrefixed(&s, &ss.secret) ||
		!s.ReadUint24LengthPrefixed(&extra) ||
		!s.ReadUint8(&extMasterSecret) ||
		!s.ReadUint8(&earlyData) ||
		len(ss.secret) == 0 ||
		!unmarshalCertificate(&s, &cert) {
		return nil, errors.New("tls: invalid session encoding")
	}
	for !extra.Empty() {
		var e []byte
		if !readUint24LengthPrefixed(&extra, &e) {
			return nil, errors.New("tls: invalid session encoding")
		}
		ss.Extra = append(ss.Extra, e)
	}
	switch typ {
	case 1:
		ss.isClient = false
	case 2:
		ss.isClient = true
	default:
		return nil, errors.New("tls: unknown session encoding")
	}
	switch extMasterSecret {
	case 0:

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Treat the cached blob as stale: discard it and perform a full handshake (do not retry ParseSessionState on the same bytes).
  2. Ensure the bytes came from SessionState.Bytes() in the same Go major version; version-stamp cached blobs if you persist them.
  3. Validate length and magic prefix before calling ParseSessionState if you control the encoding.
  4. If implementing a cache, store bytes atomically and guard against partial writes / encoding mismatches.

Example fix

// before
data := loadFromCache(key) // possibly corrupt / from old Go version
ss, err := tls.ParseSessionState(data) // -> invalid session encoding

// after
data := loadFromCache(key)
ss, err := tls.ParseSessionState(data)
if err != nil {
    cache.Delete(key) // drop stale entry, fall back to full handshake
    return nil
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Optional length sanity check before parsing cached blobs.
func looksLikeSessionState(data []byte) bool {
    // SessionState header: 2-byte version + 1-byte type + 2-byte cipher + ...
    return len(data) >= 16 // rough lower bound; refine per Go version
}

Try / catch

ss, err := tls.ParseSessionState(data)
if err != nil {
    // Treat as a stale/corrupt cache entry.
    cache.Delete(key)
    return nil // fall back to full handshake
}

Prevention

When it happens

Trigger: Calling tls.ParseSessionState on bytes that are truncated, were produced by an incompatible Go version, were corrupted in a session cache, or are not a session-state blob at all. Also when the master secret field decodes to zero length.

Common situations: Persistent ClientSessionCache that survived a Go upgrade changing the encoding; cache value mutated by an external process; deserializing a server-issued session ticket as a client SessionState (or vice versa); passing a raw NewSessionTicket message instead of a SessionState.Bytes() blob.

Understand the failure class

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/dc34a9bbc086fc15. Report an issue: GitHub.