gastownhall/beads · error

failed to re-encrypt password for peer %s: %w

Error message

failed to re-encrypt password for peer %s: %w

What it means

When re-encrypting a peer password during migration, migrateCredentialKeys calls encryptWithKey(plaintext, newKey) with the freshly generated key. This error wraps an encryption failure for the named peer. Since the new key is always a valid 32-byte AES-256 key, this fails only when the nonce read from crypto/rand fails or the crypto setup errors — rare, but aborting is required to avoid storing a password encrypted in an unusable way.

Source

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

		}

		// Decrypt with old key
		plaintext, err := decryptWithKey(encrypted, oldKey)
		if err != nil {
			// 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)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the operation — nonce-generation failures from crypto/rand are almost always transient
  2. If it persists, fix the OS entropy source (check /dev/urandom, getrandom availability, container policies)
  3. Restore the peer's plaintext password and re-add the peer (`bd` peer add) if migration left it un-migrated; the old ciphertext remains intact since the UPDATE never ran
Defensive patterns

Strategy: retry

Try / catch

if err := bdCmd(); err != nil && strings.Contains(err.Error(), "failed to re-encrypt password for peer") {
    return retryWithBackoff(bdCmd, 2) // entropy failures are transient
}

Prevention

When it happens

Trigger: encryptWithKey fails while migrating a stored peer password: io.ReadFull(rand.Reader, nonce) errors (entropy source failure) inside encryptWithKey, or aes.NewCipher/cipher.NewGCM error (only if the new key were not 32 bytes, which cannot happen from the normal generation path).

Common situations: Entropy exhaustion or a broken CSPRNG in a container at boot (same root cause as key-generation failure); custom code paths or tests supplying a wrong-length key; crypto misconfiguration in exotic environments.

Related errors


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