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

  1. Call ensureCredentialKey(ctx) (the exported path: any AddFederationPeer call) before encrypting — do not invoke encryptPassword directly on a fresh store
  2. Ensure the store's beadsDir is set to a valid .beads directory so initCredentialKey can load or create the key file
  3. If integrating the store directly, replicate the open path that runs initCredentialKey before federation operations
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/133229eb5b8f8086. Report an issue: GitHub.