gastownhall/beads · critical
failed to generate credential encryption key: %w
Error message
failed to generate credential encryption key: %w
What it means
initCredentialKey generates a fresh 32-byte AES-256 credential encryption key by reading from crypto/rand. This error wraps a failure of io.ReadFull on crypto/rand.Reader, meaning the OS entropy source could not deliver 32 random bytes. It is thrown so that bd refuses to fall back to a weak or partially-filled key, since a bad key would compromise all stored federation peer passwords.
Source
Thrown at internal/storage/dolt/credentials.go:88
// Migration: try old location (.beads/dolt/) and move to new location
if s.dbPath != "" {
oldKeyPath := filepath.Join(s.dbPath, credentialKeyFile)
oldKey, oldErr := os.ReadFile(oldKeyPath) //nolint:gosec // G304: oldKeyPath is derived from trusted dbPath
if oldErr == nil && len(oldKey) == 32 {
// Write to new location, then remove old file
if writeErr := os.WriteFile(keyPath, oldKey, 0600); writeErr == nil {
_ = os.Remove(oldKeyPath)
}
s.credentialKey = oldKey
return nil
}
}
// Generate new random 32-byte key (AES-256)
key = make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return fmt.Errorf("failed to generate credential encryption key: %w", err)
}
// Migrate existing credentials from old dbPath-derived key to new random key
if err := s.migrateCredentialKeys(ctx, key); err != nil {
return fmt.Errorf("failed to migrate credential keys: %w", err)
}
// Write key file with owner-only permissions (0600).
// Ensure the directory exists first — when connecting to an external
// server without having run `bd init`, .beads/ may not exist yet (GH#2641).
if err := os.MkdirAll(s.beadsDir, 0700); err != nil {
return fmt.Errorf("failed to create beads directory %s: %w", s.beadsDir, err)
}
if err := os.WriteFile(keyPath, key, 0600); err != nil {
return fmt.Errorf("failed to write credential key file: %w", err)
}
s.credentialKey = keyView on GitHub (pinned to 71377f2769)
Solutions
- Re-run the command — crypto/rand failures are usually transient at boot; the system RNG typically becomes available within seconds
- Check that /dev/urandom exists and is readable in the environment (ls -l /dev/urandom; on Linux verify getrandom(2) is not blocked)
- Inspect container/seccomp/AppArmor policies for blocks on getrandom and allow the syscall
- If inside a container at early boot, add an entropy-availability wait or upgrade the kernel/host so getrandom never blocks
Defensive patterns
Strategy: try-catch
Validate before calling
// Go: check the OS RNG is readable before running bd operations in constrained environments
f, err := os.Open("/dev/urandom")
if err != nil { log.Fatal("no entropy source available") }
f.Close() Try / catch
if err := runBd(); err != nil {
if strings.Contains(err.Error(), "failed to generate credential encryption key") {
// transient entropy failure: wait and retry once
time.Sleep(2 * time.Second)
err = runBd()
}
} Prevention
- Avoid running crypto workloads in entropy-starved early-boot containers; wait for RNG readiness
- Keep seccomp/AppArmor profiles permissive for getrandom(2)
- Monitor for kernel/OS updates that affect the CSPRNG
When it happens
Trigger: io.ReadFull(rand.Reader, key) fails during initCredentialKey — i.e. crypto/rand.Reader returns an error or fewer than 32 bytes. In practice only when the OS CSPRNG is unavailable or returns an error (e.g. exhausted entropy at early boot, broken /dev/urandom, seccomp/container policy blocking getrandom(2)).
Common situations: Early-boot containers or VMs with insufficient entropy; hardened seccomp/AppArmor profiles that block the getrandom syscall; unusual minimal Linux environments where /dev/urandom is inaccessible; Go runtime reporting a rand failure is otherwise extremely rare on modern systems.
Related errors
- failed to generate id: %w
- httpapi: request id seed: %w
- identity: generate request nonce: %w
- identity: generate proxy secret: %w
- multiple .doltcfg directories detected
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/83e5e51047858f17.
Report an issue: GitHub.