gastownhall/beads · error

failed to initialize credential key: %w

Error message

failed to initialize credential key: %w

What it means

addFederationPeer calls ensureCredentialKey before encrypting the peer's password; this wraps any failure from loading or generating the AES key. ensureCredentialKey reads .beads/.beads-credential-key (falling back to the legacy .beads/dolt/ location), and otherwise generates, migrates, and writes a new 32-byte key. Failures include key generation, migration, directory creation, and file write errors.

Source

Thrown at internal/storage/dolt/credentials.go:283

// This stores credentials in the database and also adds the Dolt remote.
func (s *DoltStore) AddFederationPeer(ctx context.Context, peer *storage.FederationPeer) error {
	return s.withCircuitWrite(ctx, func(ctx context.Context) error {
		return s.addFederationPeer(ctx, peer)
	})
}

func (s *DoltStore) addFederationPeer(ctx context.Context, peer *storage.FederationPeer) error {
	// Validate peer name
	if err := validatePeerName(peer.Name); err != nil {
		return fmt.Errorf("invalid peer name: %w", err)
	}

	// Encrypt password before storing
	var encryptedPwd []byte
	var err error
	if peer.Password != "" {
		if err := s.ensureCredentialKey(ctx); err != nil {
			return fmt.Errorf("failed to initialize credential key: %w", err)
		}
		encryptedPwd, err = s.encryptPassword(peer.Password)
		if err != nil {
			return fmt.Errorf("failed to encrypt password: %w", err)
		}
	}

	// Upsert the peer credentials
	_, err = s.execContext(ctx, `
		INSERT INTO federation_peers (name, remote_url, username, password_encrypted, sovereignty)
		VALUES (?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			remote_url = VALUES(remote_url),
			username = VALUES(username),
			password_encrypted = VALUES(password_encrypted),
			sovereignty = VALUES(sovereignty),
			updated_at = CURRENT_TIMESTAMP
	`, peer.Name, peer.RemoteURL, peer.Username, encryptedPwd, peer.Sovereignty)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check filesystem permissions on .beads/ and .beads/.beads-credential-key (should be 0700 dir / 0600 file, owned by the running user).
  2. If the key file is corrupt (not exactly 32 bytes), back it up and delete it, then re-add peer credentials — existing ciphertexts will be unrecoverable and peers must be re-registered with passwords.
  3. Ensure the volume holding .beads is writable; for containers, mount it read-write.
  4. Run `bd init` in the workspace first so .beads/ exists with correct permissions (GH#2641 scenario).

Example fix

// before (container)
VOLUME ["/workspace/.beads:ro"]
// after
VOLUME ["/workspace/.beads:rw"]
Defensive patterns

Strategy: validation

Validate before calling

keyPath := filepath.Join(".beads", ".beads-credential-key")
info, err := os.Stat(keyPath)
if err == nil && info.Size() != 32 {
    return fmt.Errorf("corrupt credential key file %s", keyPath)
}

Try / catch

if err := store.AddFederationPeer(ctx, peer); err != nil && strings.Contains(err.Error(), "failed to initialize credential key") {
    return fmt.Errorf("check .beads/ permissions and key file: %w", err)
}

Prevention

When it happens

Trigger: Calling AddFederationPeer with a non-empty peer.Password when the key file is unreadable/corrupt (wrong length), the .beads directory cannot be created or written (permissions, read-only filesystem), crypto/rand fails, or the legacy-credential migration fails.

Common situations: Running bd as a different user than the one who created .beads (permission denied on key file); read-only or full disk; corrupt/truncated .beads-credential-key file; containerized deployments with a non-writable mounted volume.

Related errors


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