gastownhall/beads · error
credential encryption key not initialized
Error message
credential encryption key not initialized
What it means
encryptPassword uses the store's in-memory credentialKey, which is loaded by ensureCredentialKey/initCredentialKey. This error is returned when encryptPassword is called before that initialization has happened, i.e. s.credentialKey is still nil. In normal usage addFederationPeer calls ensureCredentialKey first, so hitting this means encryption was invoked out of order or key initialization was skipped (e.g. empty beadsDir disables key loading entirely).
Source
Thrown at internal/storage/dolt/credentials.go:245
}
nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
// encryptPassword encrypts a password using AES-GCM with the store's credential key.
func (s *DoltStore) encryptPassword(password string) ([]byte, error) {
if password == "" {
return nil, nil
}
s.mu.RLock()
key := s.credentialKey
s.mu.RUnlock()
if key == nil {
return nil, fmt.Errorf("credential encryption key not initialized")
}
return encryptWithKey(password, key)
}
// decryptPassword decrypts a password using AES-GCM with the store's credential key.
func (s *DoltStore) decryptPassword(encrypted []byte) (string, error) {
if len(encrypted) == 0 {
return "", nil
}
s.mu.RLock()
key := s.credentialKey
s.mu.RUnlock()
if key == nil {
return "", fmt.Errorf("credential encryption key not initialized")
}
return decryptWithKey(encrypted, key)
}
View on GitHub (pinned to 71377f2769)
Solutions
- Call ensureCredentialKey(ctx) (the exported path: any AddFederationPeer call) before encrypting — do not invoke encryptPassword directly on a fresh store
- Ensure the store's beadsDir is set to a valid .beads directory so initCredentialKey can load or create the key file
- If integrating the store directly, replicate the open path that runs initCredentialKey before federation operations
- In tests, initialize the key explicitly (temp beadsDir) before calling encryptPassword
Example fix
// before
store.encryptPassword(pwd) // panic-path: credential encryption key not initialized
// after
if err := store.ensureCredentialKey(ctx); err != nil { return err }
enc, err := store.encryptPassword(pwd) Defensive patterns
Strategy: validation
Validate before calling
// Ensure key initialization ran before encryption
dummy, err := store.encryptPassword("")
_ = dummy
if err == nil {
// empty password short-circuits; instead verify via ensureCredentialKey:
}
if err := store.ensureCredentialKey(ctx); err != nil { return err } Try / catch
enc, err := store.encryptPassword(pwd)
if err != nil && err.Error() == "credential encryption key not initialized" {
if err := store.ensureCredentialKey(ctx); err != nil { return err }
enc, err = store.encryptPassword(pwd)
}
return enc, err Prevention
- Always go through AddFederationPeer (which calls ensureCredentialKey) rather than internal encryptPassword
- Never construct DoltStore directly in tools/tests without running the init/open sequence
- Provide a valid beadsDir so key loading is not skipped
- In tests, initialize the credential key in fixture setup
When it happens
Trigger: s.encryptPassword is called while s.credentialKey == nil: an addFederationPeer path that skipped ensureCredentialKey, a store constructed without running initCredentialKey (beadsDir empty), or code/tests calling encryptPassword directly without initialization.
Common situations: Embedding the DoltStore in another tool and calling federation APIs without the store's normal open/initialization sequence; beadsDir empty (no filesystem path), which makes initCredentialKey a no-op so the key never loads; test fixtures constructing DoltStore directly.
Related errors
- config not initialized
- failed to write credential key file: %w
- initializing Azure DevOps tracker: %w
- no database connection
- %w. Hint: run 'bd init' to create a database in the current
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/133229eb5b8f8086.
Report an issue: GitHub.