gastownhall/beads · error
failed to update encrypted password for peer %s: %w
Error message
failed to update encrypted password for peer %s: %w
What it means
After re-encrypting each peer password with the new key, migrateCredentialKeys persists it with UPDATE federation_peers SET password_encrypted = ? WHERE name = ?. This error wraps a failure of that UPDATE for the named peer. The migration aborts so the database never ends up with some peers on the new key and some on the old key under a single saved key file.
Source
Thrown at internal/storage/dolt/credentials.go:190
// Can't decrypt with old key — skip (may already use a different scheme)
continue
}
toMigrate = append(toMigrate, migrationEntry{name: name, plaintext: plaintext})
}
if err := rows.Err(); err != nil {
return fmt.Errorf("failed to iterate peers for migration: %w", err)
}
// Re-encrypt each password with the new key
for _, entry := range toMigrate {
encrypted, err := encryptWithKey(entry.plaintext, newKey)
if err != nil {
return fmt.Errorf("failed to re-encrypt password for peer %s: %w", entry.name, err)
}
if _, err := s.execContext(ctx, `
UPDATE federation_peers SET password_encrypted = ? WHERE name = ?
`, encrypted, entry.name); err != nil {
return fmt.Errorf("failed to update encrypted password for peer %s: %w", entry.name, err)
}
}
return nil
}
// encryptWithKey encrypts plaintext using AES-GCM with the given key.
func encryptWithKey(plaintext string, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Retry the operation — since the key file is only written after successful migration, the next open re-runs migration from scratch (old ciphertexts are idempotently re-encrypted)
- Check for lock contention: stop concurrent bd processes/dolt-sql-server instances, then retry
- Verify the database accepts writes and the context deadline is adequate; raise timeouts if the peer table is large
- Inspect the wrapped driver error for the exact SQL failure and fix that root cause
Defensive patterns
Strategy: retry
Validate before calling
// Pre-check writability of the table before migration-heavy operations
_, err := db.ExecContext(ctx, "SELECT 1 FROM federation_peers LIMIT 1")
if err != nil { return fmt.Errorf("federation_peers unreadable: %w", err) } Try / catch
err := bdCmd()
if err != nil && strings.Contains(err.Error(), "failed to update encrypted password for peer") {
// safe to retry: UPDATE is re-applied from intact old ciphertext on next migration
return retryWithBackoff(bdCmd, 3)
} Prevention
- Serialize bd operations that touch federation_peers across processes and servers
- Ensure the Dolt server accepts writes (not read-only) before upgrades
- Use adequate context timeouts for many-peer migrations
When it happens
Trigger: s.execContext with the UPDATE statement fails during migration: database connection dropped or context canceled/timed out mid-loop, write lock contention with another bd process or dolt-sql-server, table read-only, or a Dolt transaction/commit error on federation_peers.
Common situations: Concurrent bd commands or an external dolt-sql-server holding the write lock during `bd` open; network blip to a remote Dolt server partway through migrating several peers; context deadline exceeded on large peer tables.
Related errors
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
- failed to query orphaned dependencies: %w
- row iteration error: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9471d4a120eb3572.
Report an issue: GitHub.