{"record":{"id":"95f28f0e0221fb70","repo":"gastownhall/beads","slug":"failed-to-write-credential-key-file-w","errorCode":null,"errorMessage":"failed to write credential key file: %w","messagePattern":"failed to write credential key file: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/credentials.go","lineNumber":103,"sourceCode":"\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\n\treturn nil\n}\n\n// ensureCredentialKey lazily initializes the credential key when federation\n// operations actually need password encryption or decryption.\nfunc (s *DoltStore) ensureCredentialKey(ctx context.Context) error {\n\ts.mu.RLock()\n\tif s.credentialKey != nil {\n\t\ts.mu.RUnlock()\n\t\treturn nil\n\t}\n\ts.mu.RUnlock()\n\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L85-L121","documentation":"After generating (or migrating) the credential encryption key, initCredentialKey persists it to .beads/.beads-credential-key with 0600 permissions via os.WriteFile. This error wraps a failure of that write, meaning the key could not be saved. If it is not persisted, credentials would be encrypted with a key that is lost on process exit, so bd fails fast instead.","triggerScenarios":"os.WriteFile(keyPath, key, 0600) fails during initCredentialKey: the .beads directory exists but is not writable by the current user, the disk is full, the key file exists with restrictive ownership (written by another user), an immutable attribute is set, or the filesystem is read-only.","commonSituations":"Workspace previously used by another user so .beads-credential-key is owned by root; read-only mounts or disk-quota exhaustion; running bd via cron/CI under a service account that lacks write access to the repo's .beads directory; SELinux/AppArmor denials on the key path.","solutions":["Check ownership/permissions of .beads/.beads-credential-key and the .beads directory (ls -l .beads); fix with chown/chmod so the current user can write (0600 on the file, 0700 on the dir)","If no peer credentials need preserving, delete the stale key file so a fresh one can be written (note: existing stored passwords become undecryptable — re-add peers afterwards)","Free disk space or address quota/inode exhaustion if that is the wrapped cause","Run bd as the workspace owner rather than root/sudo, which would leave root-owned files behind"],"exampleFix":"// before\n$ ls -l .beads/.beads-credential-key\n-rw------- 1 root root ... (owned by root, bd runs as dev)\n// after\n$ sudo chown dev:dev .beads/.beads-credential-key && chmod 600 .beads/.beads-credential-key","handlingStrategy":"validation","validationCode":"keyPath := filepath.Join(\".beads\", \".beads-credential-key\")\nif info, err := os.Stat(keyPath); err == nil {\n    if st, _ := info.Sys().(*syscall.Stat_t); st != nil && st.Uid != uint32(os.Getuid()) {\n        return fmt.Errorf(\"key file owned by uid %d, current uid %d\", st.Uid, os.Getuid())\n    }\n}\nif err := os.WriteFile(\".beads/.probe\", []byte(\"x\"), 0600); err != nil {\n    return fmt.Errorf(\".beads not writable: %w\", err)\n}\nos.Remove(\".beads/.probe\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never run bd with sudo in a workspace owned by another user","Keep disk space and inodes above threshold on CI runners","Do not set immutable attributes (chattr +i) on .beads contents","Align .beads ownership when switching service accounts"],"tags":["filesystem","permissions","encryption","key-management"],"backgroundTag":"key-file-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}