hashicorp/nomad · critical
could not read from random source: %v
Error message
could not read from random source: %v
What it means
Bytes() in helper/crypto reads length bytes from crypto/rand and wraps any read failure with 'could not read from random source: %v'. The library throws it because a failing random source makes generating cryptographic keys unsafe; it refuses to return weak or partial key material. The companion 'entropy exhausted' error covers short reads.
Source
Thrown at helper/crypto/crypto.go:21
package crypto
import (
"errors"
"fmt"
// note: this is aliased so that it's more noticeable if someone
// accidentally swaps it out for math/rand via running goimports
cryptorand "crypto/rand"
)
// Bytes gets a slice of cryptographically random bytes of the given length and
// enforces that we check for short reads to avoid entropy exhaustion.
func Bytes(length int) ([]byte, error) {
key := make([]byte, length)
n, err := cryptorand.Read(key)
if err != nil {
return nil, fmt.Errorf("could not read from random source: %v", err)
}
if n < length {
return nil, errors.New("entropy exhausted")
}
return key, nil
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Check the wrapped %v cause to identify the syscall-level failure (errno) and fix that (e.g. allow getrandom in seccomp/AppArmor profile).
- Verify /dev/urandom exists and is readable in the container/host if on a legacy kernel path.
- Fix file-descriptor exhaustion (raise ulimit -n, close leaked fds) if the cause is EMFILE/ENFILE.
- Retry the operation once the OS-level entropy source is healthy; crypto/rand self-heals once the underlying syscall succeeds.
Example fix
// before
key, err := crypto.Bytes(32)
// treat as retryable fatal log
clog.Error(err)
// after
key, err := crypto.Bytes(32)
if err != nil {
// inspect cause: strings.Contains(err.Error(), "getrandom") etc.
return fmt.Errorf("key generation unavailable, check entropy source: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// No pre-call check is reliable for OS entropy; optionally preflight the source on legacy systems:
if _, err := os.Stat("/dev/urandom"); err != nil {
return fmt.Errorf("random source unavailable: %w", err)
} Type guard
func isRandomSourceError(err error) bool {
return err != nil && strings.Contains(err.Error(), "could not read from random source")
} Try / catch
key, err := crypto.Bytes(32)
if err != nil {
if isRandomSourceError(err) {
// inspect wrapped cause, fix entropy/syscall config, then retry once
}
return err
} Prevention
- Allow the getrandom/getentropy syscalls in container seccomp and LSM profiles
- Keep kernels reasonably modern so getrandom(2) is available
- Monitor fd usage; avoid EMFILE on the random source
- Treat crypto/rand failures as fatal — never fall back to math/rand for key material
When it happens
Trigger: Calling Bytes(n) (directly or via Generate, encryptDEK, or NewUnwrappedRootKey) when the underlying crypto/rand read returns an error — e.g. getrandom(2)/getentropy syscall failure, or /dev/urandom inaccessible on old platforms.
Common situations: Container sandboxes or seccomp profiles blocking getrandom; extremely old kernels lacking getrandom with /dev/urandom misconfigured; fd exhaustion preventing the random-source file from being opened; VMs under heavy entropy pressure on legacy hosts.
Related errors
- failed to generate root key: %w
- failed to generate rsa key: %w
- unable to read rooted allocation directory
- plugin not found
- plugin not executable
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/0cd27af722ea450b.
Report an issue: GitHub.