usebruno/bruno · error · Error
SafeStorage decryption failed: ${err.message}
Error message
SafeStorage decryption failed: ${err.message} What it means
Thrown by safeStorageDecrypt when Electron's safeStorage.decryptString throws — i.e. the OS keychain (Keychain on macOS, DPAPI on Windows, libsecret on Linux) rejected the ciphertext. The blob was tagged ELECTRONSAFESTORAGE_ALGO ('$00:...') but the platform could not unlock it.
Source
Thrown at packages/bruno-electron/src/utils/encryption.js:93
const encryptedString = encryptedStringBuffer.toString('hex');
return encryptedString;
}
function safeStorageDecrypt(str) {
try {
// Convert the hexadecimal string to a buffer
const encryptedStringBuffer = Buffer.from(str, 'hex');
// Decrypt the buffer
const decryptedStringBuffer = safeStorage.decryptString(encryptedStringBuffer);
// Convert the decrypted buffer to a string
const decryptedString = decryptedStringBuffer.toString();
return decryptedString;
} catch (err) {
console.error('SafeStorage decryption failed:', err);
throw new Error('SafeStorage decryption failed: ' + err.message);
}
}
function encryptString(str, passkey = null) {
if (typeof str !== 'string') {
throw new Error('Encrypt failed: invalid string');
}
if (str.length === 0) {
return '';
}
// If a passkey is provided (from cookies store), we must use it for encryption.
if (passkey !== null && passkey !== undefined) {
if (typeof passkey !== 'string' || passkey.length === 0) {
// Corrupted / empty passkey -> do not encrypt, return empty value
return '';
}
try {View on GitHub (pinned to 9bdd81c7bd)
Solutions
- Re-create the secret on the same OS user account that encrypted it; safeStorage values do not transfer across users/machines.
- On Linux, ensure a libsecret service (gnome-keyring or kwallet) is running and unlocked.
- Check safeStorage.isEncryptionAvailable() before relying on safeStorage values; re-prompt the user to re-enter if unavailable.
- Use decryptStringSafe to convert this hard failure into a recoverable {success:false} in the UI.
Example fix
// before
} catch (err) {
console.error('SafeStorage decryption failed:', err);
throw new Error('SafeStorage decryption failed: ' + err.message);
}
// after: caller uses the safe wrapper
function loadSecret(stored) {
const r = decryptStringSafe(stored);
return r.success ? r.value : null; // prompt user to re-enter
} Defensive patterns
Strategy: fallback
Validate before calling
if (safeStorage && safeStorage.isEncryptionAvailable()) {
// safeStorage decrypt is permitted
} else {
// re-prompt user; safeStorage values from another machine/user cannot be read here
} Type guard
function safeStorageUsable() {
return Boolean(safeStorage && safeStorage.isEncryptionAvailable());
} Try / catch
const { success, value } = decryptStringSafe(stored);
if (!success) {
// OS keychain cannot unlock this value (cross-user/cross-machine/reset); re-enter secret
return await promptUserToReenterSecret();
} Prevention
- safeStorage values are bound to the OS user account — never migrate them across users/machines.
- On Linux, run gnome-keyring or kwallet and keep it unlocked.
- Check isEncryptionAvailable() before relying on safeStorage-backed secrets.
When it happens
Trigger: OS credential store changed (password change, account switch, keychain reset); running on a different OS user than the one that encrypted; Linux libsecret backend unavailable; value was not actually produced by safeStorage but mis-tagged.
Common situations: User changed OS login password on Windows (DPAPI key tied to account); migrated to a new machine; Linux without gnome-keyring/kwallet; headless environment where safeStorage was available at encrypt time but not at decrypt time.
Related errors
- AES256 decryption failed: ${fallbackErr.message}
- Decrypt failed: unrecognized string format
- Decrypt failed: Invalid algo
AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13).
Data as JSON: /api/errors/bfbfea48d794d6cb.
Report an issue: GitHub.