golang/go · critical
tls: short read from Rand:
Error message
tls: short read from Rand:
What it means
Thrown by makeClientHello when io.ReadFull fails to read 32 random bytes for the ClientHello random field from config.rand(). By default, config.rand() returns crypto/rand.Reader, which reads from the operating system's CSPRNG (/dev/urandom on Linux, RtlGenRandom on Windows). A failure indicates the system entropy source is unavailable or a custom Rand reader is broken.
Source
Thrown at src/crypto/tls/handshake_client.go:107
if hello.vers > VersionTLS12 {
hello.vers = VersionTLS12
}
if c.handshakes > 0 {
hello.secureRenegotiation = c.clientFinished[:]
}
hello.cipherSuites = config.cipherSuites(hasAESGCMHardwareSupport)
// Don't advertise TLS 1.2-only cipher suites unless we're attempting TLS 1.2.
if maxVersion < VersionTLS12 {
hello.cipherSuites = slices.DeleteFunc(hello.cipherSuites, func(id uint16) bool {
return cipherSuiteByID(id).flags&suiteTLS12 != 0
})
}
_, err := io.ReadFull(config.rand(), hello.random)
if err != nil {
return nil, nil, nil, errors.New("tls: short read from Rand: " + err.Error())
}
// A random session ID is used to detect when the server accepted a ticket
// and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
// a compatibility measure (see RFC 8446, Section 4.1.2).
//
// The session ID is not set for QUIC connections (see RFC 9001, Section 8.4).
if c.quic == nil {
hello.sessionId = make([]byte, 32)
if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
return nil, nil, nil, errors.New("tls: short read from Rand: " + err.Error())
}
}
if maxVersion >= VersionTLS12 {
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms(minVersion, maxVersion)
hello.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert(minVersion, maxVersion)
}View on GitHub (pinned to b6b368adc5)
Solutions
- Do not set config.Rand at all — let the library use crypto/rand.Reader by default
- If using a custom Rand reader, ensure it implements io.Reader correctly and always returns the requested number of bytes or an error
- Ensure /dev/urandom is available and readable in the runtime environment (check container/chroot/jail configuration)
- If running in FIPS mode, verify the FIPS entropy module is properly initialized
Example fix
// before — broken custom Rand reader
config := &tls.Config{Rand: brokenReader}
// after — use default crypto/rand.Reader
config := &tls.Config{} // Rand defaults to crypto/rand.Reader Defensive patterns
Strategy: validation
Validate before calling
// Do not set config.Rand unless you have a specific reason.
// If you must, validate it can provide sufficient bytes:
func validateRandReader(r io.Reader) error {
buf := make([]byte, 32)
n, err := io.ReadFull(r, buf)
if err != nil {
return fmt.Errorf("Rand reader failed: %w", err)
}
if n != 32 {
return fmt.Errorf("Rand reader returned %d bytes, expected 32", n)
}
return nil
} Try / catch
// Prefer not setting config.Rand at all.
// If a custom Rand is needed for testing, wrap it safely:
//
// type safeRand struct{ r io.Reader }
// func (s *safeRand) Read(p []byte) (int, error) {
// return io.ReadFull(s.r, p) // guarantees full read or error
// } Prevention
- Never set config.Rand in production — use the default crypto/rand.Reader
- Ensure /dev/urandom is mounted and readable in containers and sandboxes
- For testing, use a deterministic reader that always returns the full requested length
When it happens
Trigger: config.rand() returns an error or premature EOF when asked for 32 bytes. This can happen with a custom config.Rand reader that does not implement io.ReadFull semantics, or when the OS entropy source (e.g., /dev/urandom) is inaccessible.
Common situations: Setting config.Rand to a broken or non-blocking-safe reader. Running in a heavily sandboxed container or chroot where /dev/urandom is not mounted. A FIPS module failure in fips-only mode. Extremely rare kernel entropy subsystem failures. Setting config.Rand to a deterministic reader for testing that returns fewer bytes than expected.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: either ServerName or InsecureSkipVerify must be specifi
- tls: invalid NextProtos value
- tls: NextProtos values too large
- tls: no supported versions satisfy MinVersion and MaxVersion
- tls: no supported key exchange methods (CurveIDs)
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/f52e9e66c667c32e.
Report an issue: GitHub.