gastownhall/beads · error
failed to migrate credential keys: %w
Error message
failed to migrate credential keys: %w
What it means
After generating a new random credential key, initCredentialKey calls migrateCredentialKeys to re-encrypt existing federation peer passwords from the old dbPath-derived (legacy) key to the new random key. This error wraps any failure of that migration — a failed row scan, iteration, re-encryption, or database UPDATE. The store aborts key initialization rather than leaving peer passwords in a mixed encryption scheme.
Source
Thrown at internal/storage/dolt/credentials.go:93
if oldErr == nil && len(oldKey) == 32 {
// Write to new location, then remove old file
if writeErr := os.WriteFile(keyPath, oldKey, 0600); writeErr == nil {
_ = os.Remove(oldKeyPath)
}
s.credentialKey = oldKey
return nil
}
}
// Generate new random 32-byte key (AES-256)
key = make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return fmt.Errorf("failed to generate credential encryption key: %w", err)
}
// Migrate existing credentials from old dbPath-derived key to new random key
if err := s.migrateCredentialKeys(ctx, key); err != nil {
return fmt.Errorf("failed to migrate credential keys: %w", err)
}
// Write key file with owner-only permissions (0600).
// Ensure the directory exists first — when connecting to an external
// server without having run `bd init`, .beads/ may not exist yet (GH#2641).
if err := os.MkdirAll(s.beadsDir, 0700); err != nil {
return fmt.Errorf("failed to create beads directory %s: %w", s.beadsDir, err)
}
if err := os.WriteFile(keyPath, key, 0600); err != nil {
return fmt.Errorf("failed to write credential key file: %w", err)
}
s.credentialKey = key
return nil
}
// ensureCredentialKey lazily initializes the credential key when federation
// operations actually need password encryption or decryption.View on GitHub (pinned to 71377f2769)
Solutions
- Retry the operation — migration is only attempted when no usable key file exists; once it succeeds the key file is written and migration never runs again
- Verify the Dolt database is reachable and accepting writes (run a trivial bd command like `bd ready`)
- Check for concurrent bd processes or a dolt-sql-server holding locks on federation_peers and serialize access
- Inspect the wrapped inner error (%w chain) for the exact SQL failure and address that root cause
Defensive patterns
Strategy: retry
Validate before calling
// Before upgrading, verify the DB is writable
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("dolt unavailable: %w", err) } Try / catch
err := bdCmd()
if err != nil && strings.Contains(err.Error(), "failed to migrate credential keys") {
// migration is idempotent while no key file exists — safe to retry
return retryWithBackoff(bdCmd, 3)
} Prevention
- Take a backup of .beads and the Dolt DB before upgrading across key-scheme versions
- Run one bd process at a time during upgrades to avoid lock contention
- Upgrade with a generous context timeout so migration of many peers can finish
When it happens
Trigger: initCredentialKey runs with a valid database connection and existing rows in federation_peers whose password_encrypted decrypts with the legacy key, and migrateCredentialKeys then fails: rows.Scan error, rows.Err() iteration error, encryptWithKey failure, or the UPDATE federation_peers statement errors (e.g. context canceled, connection dropped, lock timeout).
Common situations: Upgrading bd from the legacy key scheme to random keys while the Dolt database is unavailable, locked by another writer, or the context times out mid-migration; stale server connections dropping during the UPDATE loop; concurrent bd processes contending over federation_peers during open.
Related errors
- failed to update encrypted password for peer %s: %w
- failed to initialize schema: %w
- failed to rebuild pool after migration: %w
- ensuring local_metadata: %w
- reading pending ignored migrations: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c2c4e73c668e3bef.
Report an issue: GitHub.