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

  1. Check the wrapped %v cause to identify the syscall-level failure (errno) and fix that (e.g. allow getrandom in seccomp/AppArmor profile).
  2. Verify /dev/urandom exists and is readable in the container/host if on a legacy kernel path.
  3. Fix file-descriptor exhaustion (raise ulimit -n, close leaked fds) if the cause is EMFILE/ENFILE.
  4. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/0cd27af722ea450b. Report an issue: GitHub.