golang/go · error

tls: received a session ticket with empty opaque ticket labe

Error message

tls: received a session ticket with empty opaque ticket label

What it means

A TLS 1.3 client received a NewSessionTicket whose ticket field (called label internally) is empty. RFC 8446 requires the ticket to be a non-empty opaque vector; an empty value is a decode error and cannot identify a resumable session.

Source

Thrown at src/crypto/tls/handshake_client_tls13.go:859

	}

	if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
		return nil
	}

	// See RFC 8446, Section 4.6.1.
	if msg.lifetime == 0 {
		return nil
	}
	lifetime := time.Duration(msg.lifetime) * time.Second
	if lifetime > maxSessionTicketLifetime {
		c.sendAlert(alertIllegalParameter)
		return errors.New("tls: received a session ticket with invalid lifetime")
	}

	if len(msg.label) == 0 {
		c.sendAlert(alertDecodeError)
		return errors.New("tls: received a session ticket with empty opaque ticket label")
	}

	// RFC 9001, Section 4.6.1
	if c.quic != nil && msg.maxEarlyData != 0 && msg.maxEarlyData != 0xffffffff {
		c.sendAlert(alertIllegalParameter)
		return errors.New("tls: invalid early data for QUIC connection")
	}

	cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
	if cipherSuite == nil || c.resumptionSecret == nil {
		return c.sendAlert(alertInternalError)
	}

	psk := tls13.ExpandLabel(cipherSuite.hash.New, c.resumptionSecret, "resumption",
		msg.nonce, cipherSuite.hash.Size())

	session := c.sessionState()
	session.secret = psk

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Report to the server operator — RFC 8446 §4.6.1 forbids empty ticket values.
  2. If you operate the server, verify the session ticket key/seeding logic produces non-empty ciphertext.
  3. Disable session tickets on the client (SessionTicketsDisabled: true or omit ClientSessionCache) to tolerate the broken peer if resumption is not required.
  4. File an upstream issue against the offending TLS implementation.

Example fix

// client-side workaround (only if peer cannot be fixed)
cfg := &tls.Config{
    SessionTicketsDisabled: true,
}
Defensive patterns

Strategy: validation

Validate before calling

// Server-side: ensure ticket bytes are non-empty before sending.
if len(ticket.Label) == 0 {
    return errors.New("refusing to send empty session ticket")
}

Try / catch

if err != nil && strings.Contains(err.Error(), "empty opaque ticket label") {
    // disable resumption against this server or file an upstream bug
    cfg.SessionTicketsDisabled = true
}

Prevention

When it happens

Trigger: handleNewSessionTicket checks len(msg.label) == 0 after the lifetime check; if true, it sends alertDecodeError. The peer transmitted a newSessionTicketMsgTLS13 with an empty ticket body.

Common situations: A server bug generating zero-length tickets (e.g. a ticket encryptor with an empty key, a misconfigured session cache, or a stub implementation). Also seen in fuzz tests and malformed-protocol test suites.

Understand the failure class

Related errors


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