JuliusBrussee/caveman · error

native session key random: %w

Error message

native session key random: %w

What it means

LoadOrCreateSessionKey draws 32 bytes from crypto/rand for the new session key. rand.Read practically never fails on Linux (it can block until the pool is ready but errors only on a broken reader), so this error almost always indicates an extremely early boot entropy state or an OS/driver-level problem with the random device.

Source

Thrown at proxy/internal/nativeruntime/marker.go:34

const sessionKeyBytes = 32

var markerPattern = regexp.MustCompile(`\[\[caveman-session-v1 sid="([A-Za-z0-9_-]{1,384})" sig="([0-9a-f]{64})"\]\]`)

// LoadOrCreateSessionKey returns one user-only HMAC key shared by CLI adapters
// and local proxy. O_EXCL makes concurrent first startup converge on one key.
func LoadOrCreateSessionKey(home string) ([]byte, error) {
	dir := filepath.Join(home, "runtime")
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return nil, fmt.Errorf("native session key mkdir: %w", err)
	}
	if err := os.Chmod(dir, 0o700); err != nil {
		return nil, fmt.Errorf("native session key chmod dir: %w", err)
	}
	path := filepath.Join(dir, "session.key")
	key := make([]byte, sessionKeyBytes)
	if _, err := rand.Read(key); err != nil {
		return nil, fmt.Errorf("native session key random: %w", err)
	}
	file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
	if err == nil {
		if _, writeErr := file.Write(key); writeErr != nil {
			_ = file.Close()
			_ = os.Remove(path)
			return nil, fmt.Errorf("native session key write: %w", writeErr)
		}
		if syncErr := file.Sync(); syncErr != nil {
			_ = file.Close()
			_ = os.Remove(path)
			return nil, fmt.Errorf("native session key sync: %w", syncErr)
		}
		if closeErr := file.Close(); closeErr != nil {
			return nil, fmt.Errorf("native session key close: %w", closeErr)
		}
		return key, nil
	}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Simply retry after a moment — once the kernel CRNG is seeded this succeeds
  2. Ensure the container/chroot exposes a working /dev/urandom and allows getrandom(2)
  3. Use a modern base image; very old kernels lacked getrandom and fall back to weaker paths
Defensive patterns

Strategy: retry

Try / catch

var key []byte
err := backoffRetry(3, func() error {
    var e error
    key, e = nativeruntime.LoadOrCreateSessionKey(home)
    return e
}) // entropy failures self-heal once the kernel CRNG is seeded

Prevention

When it happens

Trigger: First process executed in a freshly booted minimal VM/container before the CRNG is seeded; /dev/urandom unavailable due to a broken chroot or seccomp policy; hardware/kernel fault.

Common situations: CI containers or embedded images booting and immediately starting the proxy; overly restrictive sandboxes blocking getrandom(2).

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/520db03ad275b612. Report an issue: GitHub.