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

  1. Verify the storage name in Hints matches the keychain account exactly (e.g. "Chrome Safe Storage")
  2. Dump matching records with `security find-generic-password -s "Chrome Safe Storage"` to see actual accounts
  3. Confirm the browser has been run at least once so it stored its Safe Storage key
  4. 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

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


AI-assisted analysis of moonD4rk/HackBrowserData@0503d04d7a (2026-09-06). Data as JSON: /api/errors/0153e1a437e47e8a. Report an issue: GitHub.