JuliusBrussee/caveman · critical
nonce entropy: %w
Error message
nonce entropy: %w
What it means
crypto/rand.Read failed while generating the 12-byte AES-GCM nonce in local-mode Encrypt. The kernel's CSPRNG essentially never fails on Linux/macOS in normal operation; documented failure modes are file-descriptor exhaustion (getrandom(2)/dev/urandom open fails), early boot before the entropy pool is ready on minimal VMs, or broken sandbox/seccomp profiles blocking the syscall. Since a repeated or predictable nonce destroys GCM security, the operation aborts.
Source
Thrown at shared/platform/secretbox/secretbox.go:80
}
if runtimeenv.IsProduction() {
return nil, fmt.Errorf("secretbox: production requires CAVE_KMS_PROVIDER=scaleway")
}
keyBytes, err := loadKey()
if err != nil {
return nil, err
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return nil, fmt.Errorf("aes cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("aes-gcm: %w", err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return nil, fmt.Errorf("nonce entropy: %w", err)
}
// Seal appends the ciphertext+tag to nonce, so the returned slice is the
// full nonce||ciphertext envelope.
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
// EncryptPayloadKey wraps an artifact data-encryption key. Production uses the
// dedicated payload KEK; local development retains the same AES-GCM envelope as
// other local secrets.
func EncryptPayloadKey(plaintext []byte) ([]byte, error) {
if useKMS() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
wrapped, err := kms.EncryptPayload(ctx, plaintext)
if err != nil {
return nil, fmt.Errorf("secretbox: payload KMS encrypt: %w", err)
}
return wrapped, nilView on GitHub (pinned to 27d5a3981a)
Solutions
- Check the wrapped error (errno) — EMFILE means fd exhaustion: fix the leak or raise the limit; EPERM means the sandbox blocks the syscall: allow getrandom(2)/open of /dev/urandom.
- On minimal VMs, ensure the boot path seeds entropy (haveged, virtio-rng, or waiting for the crng ready flag) before the service starts.
- This is environmental, not a code bug — retrying without fixing the environment repeats the failure.
Example fix
# before: container blocks getrandom -> "nonce entropy: operation not permitted" docker run --security-opt seccomp=broken.json mysvc # after: default profile (or one permitting getrandom) docker run mysvc
Defensive patterns
Strategy: fallback
Validate before calling
// Cheap environmental preflight: confirm the CSPRNG answers before serving.
func entropyAvailable() bool {
b := make([]byte, 1)
_, err := rand.Read(b)
return err == nil
} Try / catch
if _, err := secretbox.Encrypt(pt); err != nil {
if strings.Contains(err.Error(), "nonce entropy") {
// do NOT retry blindly: diagnose fd limits / seccomp / boot entropy; fail closed
}
} Prevention
- Run containers with the default (or a getrandom-permitting) seccomp profile; verify /dev/urandom is accessible.
- On minimal VMs, seed entropy before service start (virtio-rng, haveged, or wait for the kernel crng-ready flag).
- Watch fd usage; EMFILE from a leak can surface here first.
When it happens
Trigger: Local Encrypt under a container/sandbox whose seccomp or device-cgroup policy blocks getrandom or /dev/urandom; a very early-started process on an embedded/minimal kernel with an unseeded CRNG; ulimit -n exhausted so the random device cannot be opened.
Common situations: Custom Docker/seccomp profiles, gVisor/Firecracker microVMs at boot, sidecar-less initrd environments, or a malicious/accidental fd leak filling the process's descriptor table.
Related errors
- native session key random: %w
- probe returned false
- cave_sandbox_conformance_failed
- cave_sandbox_credential_missing
- %s is not set; cannot encrypt/decrypt secrets
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/9dc511a3e3bed169.
Report an issue: GitHub.