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

  1. 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)
  2. Check for lock contention: stop concurrent bd processes/dolt-sql-server instances, then retry
  3. Verify the database accepts writes and the context deadline is adequate; raise timeouts if the peer table is large
  4. 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

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


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