{"record":{"id":"83e5e51047858f17","repo":"gastownhall/beads","slug":"failed-to-generate-credential-encryption-key-w","errorCode":null,"errorMessage":"failed to generate credential encryption key: %w","messagePattern":"failed to generate credential encryption key: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/storage/dolt/credentials.go","lineNumber":88,"sourceCode":"\n\t// Migration: try old location (.beads/dolt/) and move to new location\n\tif s.dbPath != \"\" {\n\t\toldKeyPath := filepath.Join(s.dbPath, credentialKeyFile)\n\t\toldKey, oldErr := os.ReadFile(oldKeyPath) //nolint:gosec // G304: oldKeyPath is derived from trusted dbPath\n\t\tif oldErr == nil && len(oldKey) == 32 {\n\t\t\t// Write to new location, then remove old file\n\t\t\tif writeErr := os.WriteFile(keyPath, oldKey, 0600); writeErr == nil {\n\t\t\t\t_ = os.Remove(oldKeyPath)\n\t\t\t}\n\t\t\ts.credentialKey = oldKey\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Generate new random 32-byte key (AES-256)\n\tkey = make([]byte, 32)\n\tif _, err := io.ReadFull(rand.Reader, key); err != nil {\n\t\treturn fmt.Errorf(\"failed to generate credential encryption key: %w\", err)\n\t}\n\n\t// Migrate existing credentials from old dbPath-derived key to new random key\n\tif err := s.migrateCredentialKeys(ctx, key); err != nil {\n\t\treturn fmt.Errorf(\"failed to migrate credential keys: %w\", err)\n\t}\n\n\t// Write key file with owner-only permissions (0600).\n\t// Ensure the directory exists first — when connecting to an external\n\t// server without having run `bd init`, .beads/ may not exist yet (GH#2641).\n\tif err := os.MkdirAll(s.beadsDir, 0700); err != nil {\n\t\treturn fmt.Errorf(\"failed to create beads directory %s: %w\", s.beadsDir, err)\n\t}\n\tif err := os.WriteFile(keyPath, key, 0600); err != nil {\n\t\treturn fmt.Errorf(\"failed to write credential key file: %w\", err)\n\t}\n\n\ts.credentialKey = key","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L70-L106","documentation":"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.","triggerScenarios":"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)).","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Go: check the OS RNG is readable before running bd operations in constrained environments\nf, err := os.Open(\"/dev/urandom\")\nif err != nil { log.Fatal(\"no entropy source available\") }\nf.Close()","typeGuard":null,"tryCatchPattern":"if err := runBd(); err != nil {\n    if strings.Contains(err.Error(), \"failed to generate credential encryption key\") {\n        // transient entropy failure: wait and retry once\n        time.Sleep(2 * time.Second)\n        err = runBd()\n    }\n}","preventionTips":["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"],"tags":["crypto","entropy","key-generation","filesystem"],"backgroundTag":"crypto-rand-failure","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}