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

  1. Re-create the secret on the same OS user account that encrypted it; safeStorage values do not transfer across users/machines.
  2. On Linux, ensure a libsecret service (gnome-keyring or kwallet) is running and unlocked.
  3. Check safeStorage.isEncryptionAvailable() before relying on safeStorage values; re-prompt the user to re-enter if unavailable.
  4. 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

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


AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13). Data as JSON: /api/errors/bfbfea48d794d6cb. Report an issue: GitHub.