moonD4rk/HackBrowserData · warning
%q: %w
Error message
%q: %w
What it means
findStorageKey scanned all unlocked keychain generic-password records and found no record whose Account matches the requested storage name. It returns the sentinel errStorageNotFound wrapped with the quoted storage name. This means the browser's encryption key is not stored under that account in the keychain.
Source
Thrown at masterkey/retriever_darwin.go:75
func loadKeychainRecords(password string) ([]keychainbreaker.GenericPassword, error) {
kc, err := keychainbreaker.Open()
if err != nil {
return nil, fmt.Errorf("open keychain: %w", err)
}
if err := kc.Unlock(keychainbreaker.WithPassword(password)); err != nil {
return nil, fmt.Errorf("unlock keychain: %w", err)
}
return kc.GenericPasswords()
}
func findStorageKey(records []keychainbreaker.GenericPassword, storage string) ([]byte, error) {
for _, rec := range records {
if rec.Account == storage {
return darwinParams.deriveKey(rec.Password), nil
}
}
return nil, fmt.Errorf("%q: %w", storage, errStorageNotFound)
}
// KeychainPasswordRetriever unlocks login.keychain-db with the macOS login password (no root).
// Records are cached once and reused across browsers.
type KeychainPasswordRetriever struct {
Password string
once sync.Once
records []keychainbreaker.GenericPassword
err error
}
func (r *KeychainPasswordRetriever) RetrieveKey(hints Hints) ([]byte, error) {
if r.Password == "" {
return nil, fmt.Errorf("keychain password not provided")
}
r.once.Do(func() {View on GitHub (pinned to 0503d04d7a)
Solutions
- Verify the storage name in Hints matches the keychain account exactly (e.g. "Chrome Safe Storage")
- Dump matching records with `security find-generic-password -s "Chrome Safe Storage"` to see actual accounts
- Confirm the browser has been run at least once so it stored its Safe Storage key
- Handle errStorageNotFound via errors.Is and fall back to another retriever
Example fix
// before
key, err := retriever.RetrieveKey(hints)
return key, err
// after
key, err := retriever.RetrieveKey(hints)
if errors.Is(err, masterkey.ErrStorageNotFound) {
log.Warnf("no keychain record for %q, skipping", hints.Storage)
return nil, nil
}
return key, err Defensive patterns
Strategy: fallback
Validate before calling
out, err := exec.Command("security", "find-generic-password", "-a", storage).Output()
found := err == nil && len(out) > 0 Type guard
func isStorageNotFound(err error) bool {
return errors.Is(err, masterkey.ErrStorageNotFound)
} Try / catch
key, err := r.RetrieveKey(hints)
if errors.Is(err, masterkey.ErrStorageNotFound) {
key, err = fallbackRetriever.RetrieveKey(hints)
} Prevention
- Confirm the browser stored its Safe Storage key (launch it once)
- Match the storage account string exactly as it appears in the keychain
- Register a fallback retriever for machines without the record
When it happens
Trigger: RetrieveKey calls findStorageKey(records, storage) after a successful keychain load, and no rec.Account == storage match exists; also reachable directly in tests.
Common situations: The target browser (e.g. Chrome/Edge on macOS) never saved its Safe Storage key, the storage name in Hints doesn't match the actual keychain service/account string, or the user uses a browser version that stores keys elsewhere.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- not found in credential store
- requires root privileges
- keychain gcore dump not built in (rebuild with -tags keychai
- read keychain: %w
- tried %d candidates, none unlocked keychain
AI-assisted analysis of moonD4rk/HackBrowserData@0503d04d7a (2026-09-06).
Data as JSON: /api/errors/0153e1a437e47e8a.
Report an issue: GitHub.