# @napi-rs/keyring v2.0.0 - Product: @napi-rs/keyring (https://whatsnew.fyi/product/napi-rs-keyring) - Vendor: napi-rs - Date: 2026-08-31 - Version: v2.0.0 - Original notes: https://github.com/Brooooooklyn/keyring-node/releases/tag/v2.0.0 - Permalink: https://whatsnew.fyi/product/napi-rs-keyring/releases/v2.0.0 What's New is an index, not a publisher: every entry below links to the vendor's own release notes, which are the authoritative source. Entries are labelled where they are hand-curated sample data, pre-releases, or drawn from a secondary source such as a developer blog. Reuse: the summaries, labels and curation here are © What's New. Quote freely with attribution and a link back; wholesale republication of the corpus is not permitted — terms: https://whatsnew.fyi/terms. The vendors' own release notes remain their publishers'. --- - **changed** — getPassword() and getSecret() now throw or reject on credential store errors instead of returning null, with null reserved only for genuinely missing credentials - **changed** — deleteCredential() and deletePassword() now throw or reject on deletion failures instead of returning false, with false reserved only for missing credentials - **fixed** — preserve non-missing password read errors that were previously silenced - **fixed** — propagate credential store errors instead of erasing them into false or absent results - **changed** — async deletePassword() return type is now correctly declared as Promise instead of Promise ##### ⚠️ Breaking Changes This release changes how credential store **errors** are reported. Success and "not found" results are unchanged, but provider failures that were previously silenced now throw (sync API) or reject (async API). ###### 1. Reads: `getPassword()` / `getSecret()` Previously, **any** failure reading the credential store — a locked keychain, denied access, an OS error — was swallowed and returned as `null` / `undefined`, indistinguishable from "no credential stored". Now only a genuinely missing credential (`NoEntry`) returns the absent result. Every other error throws or rejects: | Scenario | v1.x | v2.0.0 | |---|---|---| | Credential exists | value | value (unchanged) | | Credential missing | `null` / `undefined` | `null` / `undefined` (unchanged) | | Store locked / inaccessible / OS error | `null` / `undefined` ⚠️ | **throws / rejects** | ###### 2. Deletes: `deleteCredential()` / `deletePassword()` Previously, **any** delete failure returned `false`, so a failed delete looked identical to "credential was already gone" — leaving callers unable to tell whether the secret was actually removed. Now `false` only means the credential did not exist (`NoEntry`). A failed delete throws or rejects: | Scenario | v1.x | v2.0.0 | |---|---|---| | Credential deleted | `true` | `true` (unchanged) | | No credential to delete | `false` | `false` (unchanged) | | Delete failed (locked store, OS error) | `false` ⚠️ | **throws / rejects** | A `false` result now guarantees the credential is absent from the store. ###### 3. TypeScript: async `deletePassword()` return type The async `deletePassword()` alias is now correctly declared as `Promise` instead of `Promise`. This is a narrowing and is source-compatible for typical usage, but code that treated the result as `unknown` may need a small type adjustment. These changes apply to both `Entry` (sync) and `AsyncEntry` (async), including the `deletePassword()` aliases. --- ##### 🔧 Migration Guide ###### If you only check for absent credentials — no change needed ```ts // Still works exactly as before: null means "not stored" const password = await entry.getPassword() if (password === null) { // no credential stored } ``` ###### If you call reads/deletes without error handling — add it Code that previously "worked" against a locked or inaccessible store will now surface the error. This is the main thing to audit when upgrading: ```ts // v1.x: silently got null on a locked keychain // v2.0.0: must handle the rejection try { const secret = await entry.getSecret() if (secret === null) { // credential not stored } } catch (err) { // store is locked, access denied, or OS-level failure // previously this path returned null — decide how to handle it } ``` ```ts // Deletes: distinguish "was absent" from "failed to delete" try { const deleted = await entry.deleteCredential() // deleted === true → credential was removed // deleted === false → credential did not exist (guaranteed absent) } catch (err) { // deletion failed — the credential may still be stored } ``` ###### Sync API The same applies to the sync `Entry` methods, which now **throw** on provider errors instead of returning `null` / `false`: ```ts try { const password = entry.getPassword() // null only if not stored } catch (err) { // store error } ``` ###### Why this change The new behavior matches the documented node-keytar compatibility contract: missing credentials resolve as `null`, while native failures reject. Previously `.ok()` / `.is_ok()` conversions at the N-API boundary erased that distinction, so a locked keychain looked like an empty one. See [#136](https://github.com/Brooooooklyn/keyring-node/pull/136) and [#138](https://github.com/Brooooooklyn/keyring-node/pull/138) for details. --- ##### What's Changed - fix: preserve non-missing password read errors by @skvark in [#136](https://github.com/Brooooooklyn/keyring-node/pull/136) - fix: propagate _[Truncated at 4000 characters — full notes: https://github.com/Brooooooklyn/keyring-node/releases/tag/v2.0.0]_