When downgrading to 2026.4.1 clientversions, everything works.
Also with an older server (1.35.4), there are no problems.
Expected Result
List of passwords or anything else, working SSH-Key (Client)
Actual Result
Endless loading page
After logout, I just get a white loadingpage and no more logs.
Logs
Logs, when I open Browser Extension, where I was logged in:
[2026-06-22 11:29:27.642][request][INFO] GET /api/accounts/profile
[2026-06-22 11:29:27.642][request][INFO] GET /api/accounts/profile
[2026-06-22 11:29:27.644][response][INFO] (profile) GET /api/accounts/profile => 200 OK
[2026-06-22 11:29:27.644][response][INFO] (profile) GET /api/accounts/profile => 200 OK
Screenshots or Videos
Additional Context
No response
Originally created by @fzenger7 on GitHub (Jun 22, 2026).
Original GitHub issue: https://github.com/dani-garcia/vaultwarden/issues/7361
### Prerequisites
- [x] I have searched the existing **Closed _AND_ Open** [Issues](https://github.com/dani-garcia/vaultwarden/issues?q=is%3Aissue%20) **_AND_** [Discussions](https://github.com/dani-garcia/vaultwarden/discussions?discussions_q=)
- [x] I have searched and read the [documentation](https://github.com/dani-garcia/vaultwarden/wiki/)
### Vaultwarden Support String
### Your environment (Generated via diagnostics page)
* Vaultwarden version: v1.36.0
* Web-vault version: v2026.4.1
* OS/Arch: linux/x86_64
* Running within a container: true (Base: Debian)
* Database type: SQLite
* Database version: 3.51.3
* Uses config.json: false
* Uses a reverse proxy: true
* IP Header check: true (X-Real-IP)
* Internet access: true
* Internet access via a proxy: false
* DNS Check: true
* Browser/Server Time Check: true
* Server/NTP Time Check: true
* Domain Configuration Check: true
* HTTPS Check: true
* Websocket Check: true
* HTTP Response Checks: true
### Config & Details (Generated via diagnostics page)
<details><summary>Show Config & Details</summary>
**Config:**
```json
{
"_duo_akey": null,
"_enable_duo": false,
"_enable_email_2fa": true,
"_enable_smtp": true,
"_enable_yubico": true,
"_icon_service_csp": "",
"_icon_service_url": "",
"_ip_header_enabled": true,
"_max_note_size": 10000,
"_smtp_img_src": "***:",
"admin_ratelimit_max_burst": 3,
"admin_ratelimit_seconds": 300,
"admin_session_lifetime": 20,
"admin_token": "***",
"allowed_connect_src": "",
"allowed_iframe_ancestors": "",
"attachments_folder": "data/attachments",
"auth_request_purge_schedule": "30 * * * * *",
"authenticator_disable_time_drift": false,
"data_folder": "data",
"database_conn_init": "",
"database_idle_timeout": 600,
"database_max_conns": 10,
"database_min_conns": 2,
"database_timeout": 30,
"database_url": "***************",
"db_connection_retries": 15,
"disable_2fa_remember": false,
"disable_admin_token": false,
"disable_icon_download": false,
"dns_prefer_ipv6": false,
"domain": "*****://***************",
"domain_origin": "*****://***************",
"domain_path": "",
"domain_set": true,
"duo_context_purge_schedule": "30 * * * * *",
"duo_host": null,
"duo_ikey": null,
"duo_skey": null,
"duo_use_iframe": false,
"email_2fa_auto_fallback": false,
"email_2fa_enforce_on_verified_invite": false,
"email_attempts_limit": 3,
"email_change_allowed": true,
"email_expiration_time": 600,
"email_token_size": 6,
"emergency_access_allowed": true,
"emergency_notification_reminder_schedule": "0 3 * * * *",
"emergency_request_timeout_schedule": "0 7 * * * *",
"enable_db_wal": true,
"enable_websocket": true,
"enforce_single_org_with_reset_pw_policy": false,
"event_cleanup_schedule": "0 10 0 * * *",
"events_days_retain": null,
"experimental_client_feature_flags": "ssh-key-vault-item,ssh-agent",
"extended_logging": true,
"helo_name": null,
"hibp_api_key": null,
"http_request_block_non_global_ips": true,
"http_request_block_regex": null,
"icon_blacklist_non_global_ips": true,
"icon_blacklist_regex": null,
"icon_cache_folder": "data/icon_cache",
"icon_cache_negttl": 259200,
"icon_cache_ttl": 2592000,
"icon_download_timeout": 10,
"icon_redirect_code": 302,
"icon_service": "internal",
"incomplete_2fa_schedule": "30 * * * * *",
"incomplete_2fa_time_limit": 3,
"increase_note_size_limit": false,
"invitation_expiration_hours": 120,
"invitation_org_name": "doofkopf.de",
"invitations_allowed": true,
"ip_header": "X-Real-IP",
"job_poll_interval_ms": 30000,
"log_file": null,
"log_level": "info",
"log_timestamp_format": "%Y-%m-%d %H:%M:%S.%3f",
"login_ratelimit_max_burst": 10,
"login_ratelimit_seconds": 60,
"org_attachment_limit": null,
"org_creation_users": "",
"org_events_enabled": false,
"org_groups_enabled": false,
"password_hints_allowed": true,
"password_iterations": 100000,
"purge_incomplete_sso_auth": "0 20 0 * * *",
"push_enabled": false,
"push_identity_uri": "https://identity.bitwarden.com",
"push_installation_id": "***",
"push_installation_key": "***",
"push_relay_uri": "https://push.bitwarden.com",
"reload_templates": false,
"require_device_email": false,
"rsa_key_filename": "data/rsa_key",
"send_purge_schedule": "0 5 * * * *",
"sendmail_command": null,
"sends_allowed": true,
"sends_folder": "data/sends",
"show_password_hint": false,
"signups_allowed": false,
"signups_domains_whitelist": "",
"signups_verify": true,
"signups_verify_resend_limit": 6,
"signups_verify_resend_time": 3600,
"smtp_accept_invalid_certs": false,
"smtp_accept_invalid_hostnames": false,
"smtp_auth_mechanism": null,
"smtp_debug": false,
"smtp_embed_images": true,
"smtp_explicit_tls": null,
"smtp_from": "*******************",
"smtp_from_name": "***********",
"smtp_host": "****************",
"smtp_password": "***",
"smtp_port": 587,
"smtp_security": "starttls",
"smtp_ssl": null,
"smtp_timeout": 15,
"smtp_username": "*******************",
"sso_allow_unknown_email_verification": false,
"sso_audience_trusted": null,
"sso_auth_only_not_session": false,
"sso_authority": "",
"sso_authorize_extra_params": "",
"sso_callback_path": "*****://********************************************",
"sso_client_cache_expiration": 0,
"sso_client_id": "",
"sso_client_secret": "***",
"sso_debug_tokens": false,
"sso_enabled": false,
"sso_master_password_policy": null,
"sso_only": false,
"sso_pkce": true,
"sso_scopes": "email profile",
"sso_signups_match_email": true,
"templates_folder": "data/templates",
"tmp_folder": "data/tmp",
"trash_auto_delete_days": null,
"trash_purge_schedule": "0 5 0 * * *",
"use_sendmail": false,
"use_syslog": false,
"user_attachment_limit": null,
"user_send_limit": null,
"web_vault_enabled": true,
"web_vault_folder": "web-vault/",
"yubico_client_id": null,
"yubico_secret_key": null,
"yubico_server": null
}
```
</details>
### Vaultwarden Build Version
1.36.0 (Latest Docker Tag)
### Deployment method
Official Container Image
### Custom deployment method
_No response_
### Reverse Proxy
Traefik 3.7.5
### Host/Server Operating System
Linux
### Operating System Version
Debian 13
### Clients
Browser Extension, Desktop
### Client Version
2026.5.1
### Steps To Reproduce
Browser Extension (Brave)
1. Click on the Icon in your browser
2. Don't see any passwords or anything else
Client (MacOS)
1. Open the App
2. Don't see any passwords or anything else
When downgrading to 2026.4.1 clientversions, everything works.
Also with an older server (1.35.4), there are no problems.
### Expected Result
List of passwords or anything else, working SSH-Key (Client)
### Actual Result
Endless loading page
After logout, I just get a white loadingpage and no more logs.
### Logs
```text
Logs, when I open Browser Extension, where I was logged in:
[2026-06-22 11:29:27.642][request][INFO] GET /api/accounts/profile
[2026-06-22 11:29:27.642][request][INFO] GET /api/accounts/profile
[2026-06-22 11:29:27.644][response][INFO] (profile) GET /api/accounts/profile => 200 OK
[2026-06-22 11:29:27.644][response][INFO] (profile) GET /api/accounts/profile => 200 OK
```
### Screenshots or Videos
<img width="483" height="603" alt="Image" src="https://github.com/user-attachments/assets/0713f71e-5edb-4ef3-bbb3-ec6a62ed133e" />
### Additional Context
_No response_
GiteaMirror
added the bug label 2026-07-13 20:28:37 -05:00
I too am having this same exact issue across all of my browser extensions.
<!-- gh-comment-id:4768992738 -->
@lennysh commented on GitHub (Jun 22, 2026):
I too am having this same exact issue across all of my browser extensions.
Having the same issue.
As the Bitwarden Mac app is just an electron application, the issue also applies there, but instead of showing the skeleton entries all the times it just states that there have been no entries found.
Hoping to get this resolved as quick as possible 🙏
<!-- gh-comment-id:4769559452 -->
@Kuuzoo commented on GitHub (Jun 22, 2026):
Having the same issue.
As the Bitwarden Mac app is just an electron application, the issue also applies there, but instead of showing the skeleton entries all the times it just states that there have been no entries found.
Maybe it is connected to the following PR in some way? https://github.com/dani-garcia/vaultwarden/pull/7308
Hoping to get this resolved as quick as possible 🙏
Most likely root cause: the invalid-SSH-key handling in Cipher::to_json returns sshKey: null, which the new 2026.5 SDK/WASM clients can't deserialize — so one bad SSH item blanks the whole vault list. PR #7308 looks like the right fix.
Mechanism, against current main:
src/db/models/cipher.rs:300-307: for an SSH-key cipher (type == 5) where keyFingerprint / privateKey / publicKey is missing or empty, the server sets the type data to Value::Null, so the sync response contains "sshKey": null for that item.
The 2026.5 clients are SDK/WASM-backed and model those three fields as non-optional Strings, so a null SSH key fails deserialization. Because /sync (src/api/core/ciphers.rs:143-147) returns all ciphers in one batch the client decodes together, a single un-decodable item leaves the whole list empty — matching the endless skeleton with a clean server-side 200.
This null workaround (commit dfd9e65, #5343) was originally added to keep the old native mobile clients working, so it's now a fix-turned-regression for the new clients. Downgrading the client to 2026.4.1 or the server to 1.35.4 avoids the new SDK decode path, which lines up with the report.
PR #7308 replaces the null with encrypted string placeholders for the three fields, which should let the item decode (and stay editable/deletable). One thing maintainers may want to confirm: that the placeholder approach doesn't reintroduce the original native-client breakage from #5343.
One caveat: the same "auth works, vault stuck on skeleton" symptom also appeared in #7333, which was a different cause (a malformed type-2 EncString rejected by WASM). To be sure this is the SSH path and not that one — @fzenger7, could you open the extension's DevTools console (or desktop logs) and paste the exact error line when the vault fails to load? An ssh_key / mandatory-fields error points at #7308; an EncString(...) error points at the #7333 class.
<!-- gh-comment-id:4783272085 -->
@maximilize commented on GitHub (Jun 23, 2026):
**Most likely root cause: the invalid-SSH-key handling in `Cipher::to_json` returns `sshKey: null`, which the new 2026.5 SDK/WASM clients can't deserialize — so one bad SSH item blanks the whole vault list.** PR #7308 looks like the right fix.
Mechanism, against current `main`:
- `src/db/models/cipher.rs:300-307`: for an SSH-key cipher (`type == 5`) where `keyFingerprint` / `privateKey` / `publicKey` is missing or empty, the server sets the type data to `Value::Null`, so the sync response contains `"sshKey": null` for that item.
- The 2026.5 clients are SDK/WASM-backed and model those three fields as non-optional `String`s, so a `null` SSH key fails deserialization. Because `/sync` (`src/api/core/ciphers.rs:143-147`) returns all ciphers in one batch the client decodes together, a single un-decodable item leaves the whole list empty — matching the endless skeleton with a clean server-side `200`.
- This `null` workaround (commit dfd9e65, #5343) was originally added to keep the *old native mobile* clients working, so it's now a fix-turned-regression for the new clients. Downgrading the client to 2026.4.1 or the server to 1.35.4 avoids the new SDK decode path, which lines up with the report.
PR #7308 replaces the `null` with encrypted string placeholders for the three fields, which should let the item decode (and stay editable/deletable). One thing maintainers may want to confirm: that the placeholder approach doesn't reintroduce the original native-client breakage from #5343.
One caveat: the same "auth works, vault stuck on skeleton" symptom also appeared in #7333, which was a *different* cause (a malformed type-2 `EncString` rejected by WASM). To be sure this is the SSH path and not that one — @fzenger7, could you open the extension's DevTools console (or desktop logs) and paste the exact error line when the vault fails to load? An `ssh_key` / mandatory-fields error points at #7308; an `EncString(...)` error points at the #7333 class.
Bigger question, can the SSH entries be edited/updated in any way that fixes this issue on the items themselves?
<!-- gh-comment-id:4784598115 -->
@lennysh commented on GitHub (Jun 23, 2026):
Bigger question, can the SSH entries be edited/updated in any way that fixes this issue on the items themselves?
Yes — the trigger is per-item, so fixing or removing the offending SSH-key entries clears it. From Cipher::to_json (src/db/models/cipher.rs:300-307), the server only emits sshKey: null for a cipher of type SSH key (type == 5) where any of keyFingerprint / privateKey / publicKey is missing or empty. An SSH item with all three populated serializes normally and won't break the 2026.5 clients.
The catch is you need a client that still loads the vault to do the editing: temporarily downgrade the client to 2026.4.1 (or the server to 1.35.4, as noted above), let the vault load, then either fill in the missing key fields on those SSH items or delete them.
Once no SSH-key item has an empty mandatory field, the 2026.5 client should load the full vault again. (This is just a workaround — PR #7308 is the proper server-side fix so a malformed item no longer blanks the whole list.)
<!-- gh-comment-id:4784887039 -->
@maximilize commented on GitHub (Jun 24, 2026):
Yes — the trigger is per-item, so fixing or removing the offending SSH-key entries clears it. From `Cipher::to_json` (`src/db/models/cipher.rs:300-307`), the server only emits `sshKey: null` for a cipher of type SSH key (`type == 5`) where **any** of `keyFingerprint` / `privateKey` / `publicKey` is missing or empty. An SSH item with all three populated serializes normally and won't break the 2026.5 clients.
The catch is you need a client that still loads the vault to do the editing: temporarily downgrade the client to 2026.4.1 (or the server to 1.35.4, as noted above), let the vault load, then either fill in the missing key fields on those SSH items or delete them.
Once no SSH-key item has an empty mandatory field, the 2026.5 client should load the full vault again. (This is just a workaround — PR #7308 is the proper server-side fix so a malformed item no longer blanks the whole list.)
I just checked all of my saved keys, and they all already have all three values, but I have the issue where NONE of the items will show/load in the newer clients...
<!-- gh-comment-id:4784908493 -->
@lennysh commented on GitHub (Jun 24, 2026):
What about using the vaultwarden web UI?
EDIT:
I just checked all of my saved keys, and they all already have all three values, but I have the issue where NONE of the items will show/load in the newer clients...
Good data point — if all your SSH-key items already have keyFingerprint/privateKey/publicKey populated, then the sshKey: null path (cipher.rs:300-307) isn't what's blanking your vault; you're likely hitting a different item the 2026.5 client can't deserialize. That skeleton symptom is shared across several causes — e.g. #7333 was a malformed type-2 EncString rejected by the WASM client, unrelated to SSH keys.
The thing that pins your cause is the client-side error: open the browser extension's DevTools console (or the desktop app log) when the vault fails to load and grab the exact error line. An ssh_key/mandatory-fields error points at the SSH path; an EncString(...) or other-field error points elsewhere — and would be a separate issue from this one.
On the web vault: worth trying — if it loads where the native clients don't, it at least lets you see/export items — but whether it's affected depends on which web-vault build your server bundles, so no promises.
<!-- gh-comment-id:4785013847 -->
@maximilize commented on GitHub (Jun 24, 2026):
Good data point — if all your SSH-key items already have `keyFingerprint`/`privateKey`/`publicKey` populated, then the `sshKey: null` path (`cipher.rs:300-307`) isn't what's blanking your vault; you're likely hitting a *different* item the 2026.5 client can't deserialize. That skeleton symptom is shared across several causes — e.g. #7333 was a malformed type-2 `EncString` rejected by the WASM client, unrelated to SSH keys.
The thing that pins *your* cause is the client-side error: open the browser extension's DevTools console (or the desktop app log) when the vault fails to load and grab the exact error line. An `ssh_key`/mandatory-fields error points at the SSH path; an `EncString(...)` or other-field error points elsewhere — and would be a separate issue from this one.
On the web vault: worth trying — if it loads where the native clients don't, it at least lets you see/export items — but whether it's affected depends on which web-vault build your server bundles, so no promises.
So, I found the issues I was having. It was related to two SSH Keys, both called: Test Key.
I downgraded to a working browser extension, and these two keys weren't visible there or in the vaultwarden Web UI.
Deleted them by injecting the UUID itself into the web vault UI so I could see/load them, and now everything works with the newer clients. Still have the same exact amount of SSH Keys, so idk...
Wondering what those were. I did backup the vault right before deleting just in case, lol...
EDIT:
Thinking those may have been in my Trash folder! I had a third one in there called the same exact thing, but actually does still have valid data/fields...
So for everyone coming here for answers, check your Trash folder as well!!!
<!-- gh-comment-id:4789905332 -->
@lennysh commented on GitHub (Jun 24, 2026):
So, I found the issues I was having. It was related to two SSH Keys, both called: `Test Key`.
I downgraded to a working browser extension, and these two keys weren't visible there or in the vaultwarden Web UI.
Deleted them by injecting the UUID itself into the web vault UI so I could see/load them, and now everything works with the newer clients. Still have the same exact amount of SSH Keys, so idk...
Wondering what those were. I did backup the vault right before deleting just in case, lol...
EDIT:
Thinking those may have been in my `Trash` folder! I had a third one in there called the same exact thing, but actually does still have valid data/fields...
So for everyone coming here for answers, check your `Trash` folder as well!!!
One caveat: the same "auth works, vault stuck on skeleton" symptom also appeared in #7333, which was a different cause (a malformed type-2 EncString rejected by WASM). To be sure this is the SSH path and not that one — @fzenger7, could you open the extension's DevTools console (or desktop logs) and paste the exact error line when the vault fails to load? An ssh_key / mandatory-fields error points at #7308; an EncString(...) error points at the #7333 class.
I see in the console this two errors: Uncaught Error: Error: invalid type: unit value, expected a valid string Unhandled error in angular Error: Error: invalid type: unit value, expected a valid string
<!-- gh-comment-id:4797036860 -->
@fzenger7 commented on GitHub (Jun 25, 2026):
> One caveat: the same "auth works, vault stuck on skeleton" symptom also appeared in [#7333](https://github.com/dani-garcia/vaultwarden/issues/7333), which was a _different_ cause (a malformed type-2 `EncString` rejected by WASM). To be sure this is the SSH path and not that one — [@fzenger7](https://github.com/fzenger7), could you open the extension's DevTools console (or desktop logs) and paste the exact error line when the vault fails to load? An `ssh_key` / mandatory-fields error points at [#7308](https://github.com/dani-garcia/vaultwarden/pull/7308); an `EncString(...)` error points at the [#7333](https://github.com/dani-garcia/vaultwarden/issues/7333) class.
I see in the console this two errors:
`Uncaught Error: Error: invalid type: unit value, expected a valid string`
`Unhandled error in angular Error: Error: invalid type: unit value, expected a valid string`
invalid type: unit value, expected a valid string is a third class: the WASM client's serde hitting JSON null ("unit value") where it requires a string (CipherString/EncString).
Not the sshKey: null mandatory-fields path (#7308) — that field is optional client-side, deserializes fine.
Not the malformed-EncString case (#7333) — that's a parse error on a non-null string, different message.
So one cipher has a sub-field stored as null that the 2026.5 client requires as a string. Cipher::to_json (src/db/models/cipher.rs) passes the stored data blob through largely verbatim and only guards a few known null-breaks (secure-note type, SSH mandatory fields :300-307, password-history null password :234-237, field type:205-214). A null in some other string field slips through and blanks the whole vault, since the client aborts on the first cipher it can't deserialize.
Two things pin the exact field so it can be guarded server-side: (1) the lines just above/below that console error usually name the field path (login.password, card.number, identity.*); (2) whether the offending item came from an import or a non-official client — null string sub-fields almost always do. With the field name the fix is a one-line sanitizer in to_json mirroring the existing ones.
<!-- gh-comment-id:4799582249 -->
@maximilize commented on GitHub (Jun 25, 2026):
`invalid type: unit value, expected a valid string` is a **third** class: the WASM client's serde hitting JSON `null` ("unit value") where it requires a string (`CipherString`/`EncString`).
- **Not** the `sshKey: null` mandatory-fields path (#7308) — that field is optional client-side, deserializes fine.
- **Not** the malformed-`EncString` case (#7333) — that's a parse error on a *non-null* string, different message.
So one cipher has a sub-field stored as `null` that the 2026.5 client requires as a string. `Cipher::to_json` (`src/db/models/cipher.rs`) passes the stored `data` blob through largely verbatim and only guards a few known null-breaks (secure-note `type`, SSH mandatory fields `:300-307`, password-history null password `:234-237`, field `type` `:205-214`). A `null` in some other string field slips through and blanks the whole vault, since the client aborts on the first cipher it can't deserialize.
Two things pin the exact field so it can be guarded server-side: (1) the lines just above/below that console error usually name the field path (`login.password`, `card.number`, `identity.*`); (2) whether the offending item came from an import or a non-official client — null string sub-fields almost always do. With the field name the fix is a one-line sanitizer in `to_json` mirroring the existing ones.
[TargetingRulesDataService] Update completed in 0ms
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::wasm_unlock_state: User-key in client managed state is already up to date, skipping set
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::local_user_data_key_state: No state bridge registered, skipping WrappedLocalUserDataKey migration
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::local_user_data_key_state: WrappedLocalUserDataKey already exists in state, skipping
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::local_user_data_key_state: Getting the WrappedLocalUserDataKey from state
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::crypto: User crypto initialized successfully
bitwarden_wasm_internal_bg.js:6012 INFO set_org_keys: bitwarden_core::client::encryption_settings: Decrypting organization keys
bitwarden_wasm_internal_bg.js:6012 INFO set_org_keys:decapsulate_org_key{org_id=5da6b9c8-174f-496a-b1cf-a0c9660845c1}: bitwarden_core::client::encryption_settings: Successfully decapsulated organization key for org org_id=5da6b9c8-174f-496a-b1cf-a0c9660845c1
bitwarden_wasm_internal_bg.js:6012 INFO set_org_keys:decapsulate_org_key{org_id=821e6c30-887b-42ef-bf8e-d183d70039cd}: bitwarden_core::client::encryption_settings: Successfully decapsulated organization key for org org_id=821e6c30-887b-42ef-bf8e-d183d70039cd
bitwarden_wasm_internal_bg.js:5648 Uncaught Error: Error: invalid type: unit value, expected a valid string
at oe (bitwarden_wasm_internal_bg.js:5648:11)
at 8204e5796ed42e246ba4.module.wasm:0x4a8e06
at 8204e5796ed42e246ba4.module.wasm:0x457eb7
at 8204e5796ed42e246ba4.module.wasm:0x37a00d
at 8204e5796ed42e246ba4.module.wasm:0x39e6d3
at 8204e5796ed42e246ba4.module.wasm:0x459806
at Rn (bitwarden_wasm_internal_bg.js:6922:14)
at a (bitwarden_wasm_internal_bg.js:7260:20)
console-log.service.ts:43 [TargetingRulesDataService] Update completed in 0ms
browser-sdk-load.service.ts:35 WebAssembly is supported in this environment
console-log.service.ts:43 [BrowserApi] Message sender appears to be internal
console-log.service.ts:43 WASM SDK loaded in 50ms
console-log.service.ts:43 State version: 78
console-log.service.ts:43 Force redraw is on
console-log.service.ts:43 [EncryptedMigrationsScheduler] No migrations needed for user 5d4f7dd2-373f-40f1-95d5-5519ccf745d7
This are the lines above and below the error, but I can't see anything pointing to the broken field.
<!-- gh-comment-id:4806725487 -->
@fzenger7 commented on GitHub (Jun 26, 2026):
```
[TargetingRulesDataService] Update completed in 0ms
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::wasm_unlock_state: User-key in client managed state is already up to date, skipping set
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::local_user_data_key_state: No state bridge registered, skipping WrappedLocalUserDataKey migration
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::local_user_data_key_state: WrappedLocalUserDataKey already exists in state, skipping
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::local_user_data_key_state: Getting the WrappedLocalUserDataKey from state
bitwarden_wasm_internal_bg.js:6012 INFO initialize_user_crypto: bitwarden_core::key_management::crypto: User crypto initialized successfully
bitwarden_wasm_internal_bg.js:6012 INFO set_org_keys: bitwarden_core::client::encryption_settings: Decrypting organization keys
bitwarden_wasm_internal_bg.js:6012 INFO set_org_keys:decapsulate_org_key{org_id=5da6b9c8-174f-496a-b1cf-a0c9660845c1}: bitwarden_core::client::encryption_settings: Successfully decapsulated organization key for org org_id=5da6b9c8-174f-496a-b1cf-a0c9660845c1
bitwarden_wasm_internal_bg.js:6012 INFO set_org_keys:decapsulate_org_key{org_id=821e6c30-887b-42ef-bf8e-d183d70039cd}: bitwarden_core::client::encryption_settings: Successfully decapsulated organization key for org org_id=821e6c30-887b-42ef-bf8e-d183d70039cd
bitwarden_wasm_internal_bg.js:5648 Uncaught Error: Error: invalid type: unit value, expected a valid string
at oe (bitwarden_wasm_internal_bg.js:5648:11)
at 8204e5796ed42e246ba4.module.wasm:0x4a8e06
at 8204e5796ed42e246ba4.module.wasm:0x457eb7
at 8204e5796ed42e246ba4.module.wasm:0x37a00d
at 8204e5796ed42e246ba4.module.wasm:0x39e6d3
at 8204e5796ed42e246ba4.module.wasm:0x459806
at Rn (bitwarden_wasm_internal_bg.js:6922:14)
at a (bitwarden_wasm_internal_bg.js:7260:20)
console-log.service.ts:43 [TargetingRulesDataService] Update completed in 0ms
browser-sdk-load.service.ts:35 WebAssembly is supported in this environment
console-log.service.ts:43 [BrowserApi] Message sender appears to be internal
console-log.service.ts:43 WASM SDK loaded in 50ms
console-log.service.ts:43 State version: 78
console-log.service.ts:43 Force redraw is on
console-log.service.ts:43 [EncryptedMigrationsScheduler] No migrations needed for user 5d4f7dd2-373f-40f1-95d5-5519ccf745d7
```
This are the lines above and below the error, but I can't see anything pointing to the broken field.
That trace can't name the field — it's compiled WASM offsets (bitwarden_wasm_internal_bg.js / module.wasm:0x…), and serde aborts on the first cipher it can't decode, so the console won't point at the culprit.
The reliable place to find it is the server's stored data, not the client. In vaultwarden the cipher field names are plaintext while the values are encrypted, so we can list which fields hold a literal null without exposing anything secret. On the host with your SQLite DB (data/db.sqlite3), with jq available:
sqlite3 -json data/db.sqlite3 \
"SELECT atype, data, fields, (deleted_at IS NOT NULL) AS trash FROM ciphers;"\
| jq -r '.[] | . as $c
| ([ ($c.data|fromjson|to_entries[]|select(.value==null)|.key) ]
+ [ ($c.fields // "[]"|fromjson)[]? | select(.value==null) | "customField.value" ]) as $n
| select($n|length>0)
| "type=\($c.atype) trash=\($c.trash) null: \($n|join(", "))"'
Cipher types: 1=login, 2=secureNote, 3=card, 4=identity, 5=sshKey. Include trash — @lennysh's culprit lived in the Trash folder, and deleted items are still in /sync.
A few nulls are normal/harmless (already guarded or optional in to_json — e.g. totp, passwordRevisionDate, uri, match, passwordHistory[].password, and the SSH/field-type cases). The interesting line is a string sub-field on a non-trash or trash item that's null where the 2026.5 SDK wants a string — the most common offenders are items created/imported by a non-official client. If you paste the output here I can map it to the exact field.
Once it's pinned, the server-side fix is a one-line sanitizer in Cipher::to_json (src/db/models/cipher.rs) mirroring the existing guards (:205-214 field type, :288-294 secure-note type, :301-307 SSH fields) — so a single malformed item can no longer blank the whole vault.
<!-- gh-comment-id:4807352919 -->
@maximilize commented on GitHub (Jun 26, 2026):
That trace can't name the field — it's compiled WASM offsets (`bitwarden_wasm_internal_bg.js` / `module.wasm:0x…`), and serde aborts on the first cipher it can't decode, so the console won't point at the culprit.
The reliable place to find it is the **server's stored data**, not the client. In vaultwarden the cipher field *names* are plaintext while the *values* are encrypted, so we can list which fields hold a literal `null` without exposing anything secret. On the host with your SQLite DB (`data/db.sqlite3`), with `jq` available:
```bash
sqlite3 -json data/db.sqlite3 \
"SELECT atype, data, fields, (deleted_at IS NOT NULL) AS trash FROM ciphers;" \
| jq -r '.[] | . as $c
| ([ ($c.data|fromjson|to_entries[]|select(.value==null)|.key) ]
+ [ ($c.fields // "[]"|fromjson)[]? | select(.value==null) | "customField.value" ]) as $n
| select($n|length>0)
| "type=\($c.atype) trash=\($c.trash) null: \($n|join(", "))"'
```
Cipher types: `1`=login, `2`=secureNote, `3`=card, `4`=identity, `5`=sshKey. **Include trash** — @lennysh's culprit lived in the Trash folder, and deleted items are still in `/sync`.
A few `null`s are normal/harmless (already guarded or optional in `to_json` — e.g. `totp`, `passwordRevisionDate`, `uri`, `match`, `passwordHistory[].password`, and the SSH/field-`type` cases). The interesting line is a **string** sub-field on a non-trash *or* trash item that's `null` where the 2026.5 SDK wants a string — the most common offenders are items created/imported by a non-official client. If you paste the output here I can map it to the exact field.
Once it's pinned, the server-side fix is a one-line sanitizer in `Cipher::to_json` (`src/db/models/cipher.rs`) mirroring the existing guards (`:205-214` field `type`, `:288-294` secure-note `type`, `:301-307` SSH fields) — so a single malformed item can no longer blank the whole vault.
As talked about in the following comment on the before linked PR, I was able to resolve the not loading vault by clearing my empty SSH keys from my bin.
I've had multiple of the following SSH keys which only had a name, but no actual key. Deleting those from the bin via the mobile app/web dashboard did fully resolve it for me.
I think in general it would still be good to have some sort of other way to resolve this/at least inform users if they have keys which cause those issues.
<!-- gh-comment-id:4825434061 -->
@Kuuzoo commented on GitHub (Jun 28, 2026):
As talked about in the following comment on the before linked PR, I was able to resolve the not loading vault by clearing my empty SSH keys from my bin.
I've had multiple of the following SSH keys which only had a name, but no actual key. Deleting those from the bin via the mobile app/web dashboard did fully resolve it for me.
I think in general it would still be good to have some sort of other way to resolve this/at least inform users if they have keys which cause those issues.

https://github.com/dani-garcia/vaultwarden/pull/7308#issuecomment-4825230885
For anyone landing here: the trigger is an SSH-key item (cipher type 5) that's missing its key material — typically one left in the Trash.
On /sync the server already tries to "fix" such items, but the current code blanks the whole sshKey object to null — in Cipher::to_json (src/db/models/cipher.rs), the self.atype == 5 && (keyFingerprint/privateKey/publicKey is_none_or empty) branch sets type_data_json = Value::Null. That null is exactly what the 2026.5 SDK can't decode: invalid type: unit value, expected a valid string. serde aborts on the first cipher it can't decode, so one bad item takes the whole vault down. Deleted/trash items are included in /sync (find_by_user_visible filters on access, not on deleted_at) and run through the same to_json, which is why clearing the empty keys from the bin resolves it.
#7308 fixes precisely this: instead of null it emits a string placeholder for the mandatory SSH fields, so the SDK can decode the item (and you can then open and delete it). Since it runs in to_json, it covers the trash case too. #7361 is resolved by #7308 and can be closed against it.
<!-- gh-comment-id:4825596916 -->
@maximilize commented on GitHub (Jun 28, 2026):
For anyone landing here: the trigger is an SSH-key item (cipher type 5) that's missing its key material — typically one left in the Trash.
On `/sync` the server already tries to "fix" such items, but the current code blanks the whole `sshKey` object to `null` — in `Cipher::to_json` (`src/db/models/cipher.rs`), the `self.atype == 5 && (keyFingerprint/privateKey/publicKey is_none_or empty)` branch sets `type_data_json = Value::Null`. That `null` is exactly what the 2026.5 SDK can't decode: `invalid type: unit value, expected a valid string`. serde aborts on the first cipher it can't decode, so one bad item takes the whole vault down. Deleted/trash items are included in `/sync` (`find_by_user_visible` filters on access, not on `deleted_at`) and run through the same `to_json`, which is why clearing the empty keys from the bin resolves it.
#7308 fixes precisely this: instead of `null` it emits a string placeholder for the mandatory SSH fields, so the SDK can decode the item (and you can then open and delete it). Since it runs in `to_json`, it covers the trash case too. #7361 is resolved by #7308 and can be closed against it.
@coderunner34-ahruphey commented on GitHub (Jul 8, 2026):
That issues was also by my install,
Fix:
Update the server to 1.36.0 and reinstall the browser extension to 2026.6.1.
<!-- gh-comment-id:4918886213 -->
@coderunner34-ahruphey commented on GitHub (Jul 8, 2026):
That issues was also by my install,
Fix:
Update the server to 1.36.0 and reinstall the browser extension to 2026.6.1.
sqlite3 -json data/db.sqlite3
"SELECT atype, data, fields, (deleted_at IS NOT NULL) AS trash FROM ciphers;"
| jq -r '.[] | . as $c
| ([ ($c.data|fromjson|to_entries[]|select(.value==null)|.key) ]
+ [ ($c.fields // "[]"|fromjson)[]? | select(.value==null) | "customField.value" ]) as $n
| select($n|length>0)
| "type=($c.atype) trash=($c.trash) null: ($n|join(", "))"'
Cipher types: 1=login, 2=secureNote, 3=card, 4=identity, 5=sshKey. Include trash — @lennysh's culprit lived in the Trash folder, and deleted items are still in /sync.
A few nulls are normal/harmless (already guarded or optional in to_json — e.g. totp, passwordRevisionDate, uri, match, passwordHistory[].password, and the SSH/field-type cases). The interesting line is a string sub-field on a non-trash or trash item that's null where the 2026.5 SDK wants a string — the most common offenders are items created/imported by a non-official client. If you paste the output here I can map it to the exact field.
Once it's pinned, the server-side fix is a one-line sanitizer in Cipher::to_json (src/db/models/cipher.rs) mirroring the existing guards (:205-214 field type, :288-294 secure-note type, :301-307 SSH fields) — so a single malformed item can no longer blank the whole vault.
@maximilize I found and removed an SSH key that was probably causing issues but I am still unable to load the browser extension because of this issue (not the encString issue). I ran the command you provided, this is the bottom of the output:
Have any other pearls of wisdom I could use to identify the issue?
Edit:
Here is the bottom line (363) of the JSON output from the sqlite3 query:
{"atype":5,"data":"","fields":null,"trash":1}]
Edit 2:
I fixed it by confirming the cipher was not foreign key linked to any other table then deleting it manually.
The cipher did not show up in the web browser trash folder, and the app trash folder showed it but crashed when i tried to interact with it.
So I pulled a:
sqlite3-jsondb.sqlite3\"DELETE FROM ciphers WHERE uuid = 'MY_UUID_HERE';"
I am assuming that if I updated the data column to be valid JSON instead of an empty string, I would have been able to interact with it in the app. But that is just a guess.
<!-- gh-comment-id:4926163372 -->
@SamuelWhittle commented on GitHub (Jul 9, 2026):
> sqlite3 -json data/db.sqlite3 \
> "SELECT atype, data, fields, (deleted_at IS NOT NULL) AS trash FROM ciphers;" \
> | jq -r '.[] | . as $c
> | ([ ($c.data|fromjson|to_entries[]|select(.value==null)|.key) ]
> + [ ($c.fields // "[]"|fromjson)[]? | select(.value==null) | "customField.value" ]) as $n
> | select($n|length>0)
> | "type=\($c.atype) trash=\($c.trash) null: \($n|join(", "))"'
>
> Cipher types: `1`=login, `2`=secureNote, `3`=card, `4`=identity, `5`=sshKey. **Include trash** — [@lennysh](https://github.com/lennysh)'s culprit lived in the Trash folder, and deleted items are still in `/sync`.
>
> A few `null`s are normal/harmless (already guarded or optional in `to_json` — e.g. `totp`, `passwordRevisionDate`, `uri`, `match`, `passwordHistory[].password`, and the SSH/field-`type` cases). The interesting line is a **string** sub-field on a non-trash _or_ trash item that's `null` where the 2026.5 SDK wants a string — the most common offenders are items created/imported by a non-official client. If you paste the output here I can map it to the exact field.
>
> Once it's pinned, the server-side fix is a one-line sanitizer in `Cipher::to_json` (`src/db/models/cipher.rs`) mirroring the existing guards (`:205-214` field `type`, `:288-294` secure-note `type`, `:301-307` SSH fields) — so a single malformed item can no longer blank the whole vault.
@maximilize I found and removed an SSH key that was probably causing issues but I am still unable to load the browser extension because of this issue (not the encString issue). I ran the command you provided, this is the bottom of the output:
```
type=1 trash=0 null: totp
type=1 trash=1 null: autofillOnPageLoad, passwordRevisionDate, totp
type=1 trash=0 null: autofillOnPageLoad, totp
type=1 trash=0 null: autofillOnPageLoad, passwordRevisionDate, totp
type=1 trash=0 null: autofillOnPageLoad, passwordRevisionDate, totp
type=1 trash=0 null: autofillOnPageLoad, passwordRevisionDate, totp
type=1 trash=0 null: autofillOnPageLoad, passwordRevisionDate, totp
type=1 trash=0 null: totp
type=1 trash=0 null: autofillOnPageLoad, totp
type=1 trash=0 null: autofillOnPageLoad, passwordRevisionDate, totp
type=1 trash=0 null: autofillOnPageLoad, passwordRevisionDate, totp
jq: error (at <stdin>:364): Expected JSON value (while parsing '')
```
Have any other pearls of wisdom I could use to identify the issue?
Edit:
Here is the bottom line (363) of the JSON output from the sqlite3 query:
```JSON
{"atype":5,"data":"","fields":null,"trash":1}]
```
Edit 2:
I fixed it by confirming the cipher was not foreign key linked to any other table then deleting it manually.
The cipher did not show up in the web browser trash folder, and the app trash folder showed it but crashed when i tried to interact with it.
So I pulled a:
```SQL
sqlite3 -json db.sqlite3 \
"DELETE FROM ciphers WHERE uuid = 'MY_UUID_HERE';"
```
I am assuming that if I updated the data column to be valid JSON instead of an empty string, I would have been able to interact with it in the app. But that is just a guess.
That last line is your culprit: {"atype":5,"data":"","trash":1} — an SSH-key item (type 5) sitting in Trash with an empty data field.
Trace through Cipher::to_json (src/db/models/cipher.rs): the empty data fails to parse, so it falls back to {} (:253-255); the SSH guard then sees the mandatory key fields missing and blanks the whole entry to null (:300-306), emitted as "sshKey": null (:410). That null is exactly the invalid type: unit value, expected a valid string your 2026.5 client aborts on — so this single item blanks the entire vault. It's the same SSH-in-Trash class as @lennysh/@Kuuzoo, not the EncString (#7333) one.
Fix: delete that item from Trash using a client/web-vault that still loads (downgrade to 2026.4.1 as before, empty the Trash, then re-upgrade). #7308 is the proper server-side fix and covers the Trash path.
The jq: Expected JSON value error is just the empty data breaking fromjson in the diagnostic, not a separate problem.
<!-- gh-comment-id:4927682225 -->
@maximilize commented on GitHub (Jul 9, 2026):
That last line is your culprit: `{"atype":5,"data":"","trash":1}` — an SSH-key item (type 5) sitting in **Trash** with an empty `data` field.
Trace through `Cipher::to_json` (`src/db/models/cipher.rs`): the empty `data` fails to parse, so it falls back to `{}` (`:253-255`); the SSH guard then sees the mandatory key fields missing and blanks the whole entry to `null` (`:300-306`), emitted as `"sshKey": null` (`:410`). That `null` is exactly the `invalid type: unit value, expected a valid string` your 2026.5 client aborts on — so this single item blanks the entire vault. It's the same SSH-in-Trash class as @lennysh/@Kuuzoo, not the `EncString` (#7333) one.
Fix: delete that item from Trash using a client/web-vault that still loads (downgrade to 2026.4.1 as before, empty the Trash, then re-upgrade). #7308 is the proper server-side fix and covers the Trash path.
The `jq: Expected JSON value` error is just the empty `data` breaking `fromjson` in the diagnostic, not a separate problem.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @fzenger7 on GitHub (Jun 22, 2026).
Original GitHub issue: https://github.com/dani-garcia/vaultwarden/issues/7361
Prerequisites
Vaultwarden Support String
Your environment (Generated via diagnostics page)
Config & Details (Generated via diagnostics page)
Show Config & Details
Config:
Vaultwarden Build Version
1.36.0 (Latest Docker Tag)
Deployment method
Official Container Image
Custom deployment method
No response
Reverse Proxy
Traefik 3.7.5
Host/Server Operating System
Linux
Operating System Version
Debian 13
Clients
Browser Extension, Desktop
Client Version
2026.5.1
Steps To Reproduce
Browser Extension (Brave)
Client (MacOS)
When downgrading to 2026.4.1 clientversions, everything works.
Also with an older server (1.35.4), there are no problems.
Expected Result
List of passwords or anything else, working SSH-Key (Client)
Actual Result
Endless loading page
After logout, I just get a white loadingpage and no more logs.
Logs
Screenshots or Videos
Additional Context
No response
@lennysh commented on GitHub (Jun 22, 2026):
I too am having this same exact issue across all of my browser extensions.
@Kuuzoo commented on GitHub (Jun 22, 2026):
Having the same issue.
As the Bitwarden Mac app is just an electron application, the issue also applies there, but instead of showing the skeleton entries all the times it just states that there have been no entries found.
Maybe it is connected to the following PR in some way? https://github.com/dani-garcia/vaultwarden/pull/7308
Hoping to get this resolved as quick as possible 🙏
@breiti78 commented on GitHub (Jun 23, 2026):
Same issue here. browser extension connects but no entries.
@maximilize commented on GitHub (Jun 23, 2026):
Most likely root cause: the invalid-SSH-key handling in
Cipher::to_jsonreturnssshKey: null, which the new 2026.5 SDK/WASM clients can't deserialize — so one bad SSH item blanks the whole vault list. PR #7308 looks like the right fix.Mechanism, against current
main:src/db/models/cipher.rs:300-307: for an SSH-key cipher (type == 5) wherekeyFingerprint/privateKey/publicKeyis missing or empty, the server sets the type data toValue::Null, so the sync response contains"sshKey": nullfor that item.Strings, so anullSSH key fails deserialization. Because/sync(src/api/core/ciphers.rs:143-147) returns all ciphers in one batch the client decodes together, a single un-decodable item leaves the whole list empty — matching the endless skeleton with a clean server-side200.nullworkaround (commitdfd9e65, #5343) was originally added to keep the old native mobile clients working, so it's now a fix-turned-regression for the new clients. Downgrading the client to 2026.4.1 or the server to 1.35.4 avoids the new SDK decode path, which lines up with the report.PR #7308 replaces the
nullwith encrypted string placeholders for the three fields, which should let the item decode (and stay editable/deletable). One thing maintainers may want to confirm: that the placeholder approach doesn't reintroduce the original native-client breakage from #5343.One caveat: the same "auth works, vault stuck on skeleton" symptom also appeared in #7333, which was a different cause (a malformed type-2
EncStringrejected by WASM). To be sure this is the SSH path and not that one — @fzenger7, could you open the extension's DevTools console (or desktop logs) and paste the exact error line when the vault fails to load? Anssh_key/ mandatory-fields error points at #7308; anEncString(...)error points at the #7333 class.@lennysh commented on GitHub (Jun 23, 2026):
Bigger question, can the SSH entries be edited/updated in any way that fixes this issue on the items themselves?
@maximilize commented on GitHub (Jun 24, 2026):
Yes — the trigger is per-item, so fixing or removing the offending SSH-key entries clears it. From
Cipher::to_json(src/db/models/cipher.rs:300-307), the server only emitssshKey: nullfor a cipher of type SSH key (type == 5) where any ofkeyFingerprint/privateKey/publicKeyis missing or empty. An SSH item with all three populated serializes normally and won't break the 2026.5 clients.The catch is you need a client that still loads the vault to do the editing: temporarily downgrade the client to 2026.4.1 (or the server to 1.35.4, as noted above), let the vault load, then either fill in the missing key fields on those SSH items or delete them.
Once no SSH-key item has an empty mandatory field, the 2026.5 client should load the full vault again. (This is just a workaround — PR #7308 is the proper server-side fix so a malformed item no longer blanks the whole list.)
@lennysh commented on GitHub (Jun 24, 2026):
What about using the vaultwarden web UI?
EDIT:
I just checked all of my saved keys, and they all already have all three values, but I have the issue where NONE of the items will show/load in the newer clients...
@maximilize commented on GitHub (Jun 24, 2026):
Good data point — if all your SSH-key items already have
keyFingerprint/privateKey/publicKeypopulated, then thesshKey: nullpath (cipher.rs:300-307) isn't what's blanking your vault; you're likely hitting a different item the 2026.5 client can't deserialize. That skeleton symptom is shared across several causes — e.g. #7333 was a malformed type-2EncStringrejected by the WASM client, unrelated to SSH keys.The thing that pins your cause is the client-side error: open the browser extension's DevTools console (or the desktop app log) when the vault fails to load and grab the exact error line. An
ssh_key/mandatory-fields error points at the SSH path; anEncString(...)or other-field error points elsewhere — and would be a separate issue from this one.On the web vault: worth trying — if it loads where the native clients don't, it at least lets you see/export items — but whether it's affected depends on which web-vault build your server bundles, so no promises.
@lennysh commented on GitHub (Jun 24, 2026):
So, I found the issues I was having. It was related to two SSH Keys, both called:
Test Key.I downgraded to a working browser extension, and these two keys weren't visible there or in the vaultwarden Web UI.
Deleted them by injecting the UUID itself into the web vault UI so I could see/load them, and now everything works with the newer clients. Still have the same exact amount of SSH Keys, so idk...
Wondering what those were. I did backup the vault right before deleting just in case, lol...
EDIT:
Thinking those may have been in my
Trashfolder! I had a third one in there called the same exact thing, but actually does still have valid data/fields...So for everyone coming here for answers, check your
Trashfolder as well!!!@fzenger7 commented on GitHub (Jun 25, 2026):
I see in the console this two errors:
Uncaught Error: Error: invalid type: unit value, expected a valid stringUnhandled error in angular Error: Error: invalid type: unit value, expected a valid string@maximilize commented on GitHub (Jun 25, 2026):
invalid type: unit value, expected a valid stringis a third class: the WASM client's serde hitting JSONnull("unit value") where it requires a string (CipherString/EncString).sshKey: nullmandatory-fields path (#7308) — that field is optional client-side, deserializes fine.EncStringcase (#7333) — that's a parse error on a non-null string, different message.So one cipher has a sub-field stored as
nullthat the 2026.5 client requires as a string.Cipher::to_json(src/db/models/cipher.rs) passes the storeddatablob through largely verbatim and only guards a few known null-breaks (secure-notetype, SSH mandatory fields:300-307, password-history null password:234-237, fieldtype:205-214). Anullin some other string field slips through and blanks the whole vault, since the client aborts on the first cipher it can't deserialize.Two things pin the exact field so it can be guarded server-side: (1) the lines just above/below that console error usually name the field path (
login.password,card.number,identity.*); (2) whether the offending item came from an import or a non-official client — null string sub-fields almost always do. With the field name the fix is a one-line sanitizer into_jsonmirroring the existing ones.@fzenger7 commented on GitHub (Jun 26, 2026):
This are the lines above and below the error, but I can't see anything pointing to the broken field.
@maximilize commented on GitHub (Jun 26, 2026):
That trace can't name the field — it's compiled WASM offsets (
bitwarden_wasm_internal_bg.js/module.wasm:0x…), and serde aborts on the first cipher it can't decode, so the console won't point at the culprit.The reliable place to find it is the server's stored data, not the client. In vaultwarden the cipher field names are plaintext while the values are encrypted, so we can list which fields hold a literal
nullwithout exposing anything secret. On the host with your SQLite DB (data/db.sqlite3), withjqavailable:Cipher types:
1=login,2=secureNote,3=card,4=identity,5=sshKey. Include trash — @lennysh's culprit lived in the Trash folder, and deleted items are still in/sync.A few
nulls are normal/harmless (already guarded or optional into_json— e.g.totp,passwordRevisionDate,uri,match,passwordHistory[].password, and the SSH/field-typecases). The interesting line is a string sub-field on a non-trash or trash item that'snullwhere the 2026.5 SDK wants a string — the most common offenders are items created/imported by a non-official client. If you paste the output here I can map it to the exact field.Once it's pinned, the server-side fix is a one-line sanitizer in
Cipher::to_json(src/db/models/cipher.rs) mirroring the existing guards (:205-214fieldtype,:288-294secure-notetype,:301-307SSH fields) — so a single malformed item can no longer blank the whole vault.@Kuuzoo commented on GitHub (Jun 28, 2026):
As talked about in the following comment on the before linked PR, I was able to resolve the not loading vault by clearing my empty SSH keys from my bin.
I've had multiple of the following SSH keys which only had a name, but no actual key. Deleting those from the bin via the mobile app/web dashboard did fully resolve it for me.
I think in general it would still be good to have some sort of other way to resolve this/at least inform users if they have keys which cause those issues.
https://github.com/dani-garcia/vaultwarden/pull/7308#issuecomment-4825230885
@maximilize commented on GitHub (Jun 28, 2026):
For anyone landing here: the trigger is an SSH-key item (cipher type 5) that's missing its key material — typically one left in the Trash.
On
/syncthe server already tries to "fix" such items, but the current code blanks the wholesshKeyobject tonull— inCipher::to_json(src/db/models/cipher.rs), theself.atype == 5 && (keyFingerprint/privateKey/publicKey is_none_or empty)branch setstype_data_json = Value::Null. Thatnullis exactly what the 2026.5 SDK can't decode:invalid type: unit value, expected a valid string. serde aborts on the first cipher it can't decode, so one bad item takes the whole vault down. Deleted/trash items are included in/sync(find_by_user_visiblefilters on access, not ondeleted_at) and run through the sameto_json, which is why clearing the empty keys from the bin resolves it.#7308 fixes precisely this: instead of
nullit emits a string placeholder for the mandatory SSH fields, so the SDK can decode the item (and you can then open and delete it). Since it runs into_json, it covers the trash case too. #7361 is resolved by #7308 and can be closed against it.@coderunner34-ahruphey commented on GitHub (Jul 8, 2026):
That issues was also by my install,
Fix:
Update the server to 1.36.0 and reinstall the browser extension to 2026.6.1.
@SamuelWhittle commented on GitHub (Jul 9, 2026):
@maximilize I found and removed an SSH key that was probably causing issues but I am still unable to load the browser extension because of this issue (not the encString issue). I ran the command you provided, this is the bottom of the output:
Have any other pearls of wisdom I could use to identify the issue?
Edit:
Here is the bottom line (363) of the JSON output from the sqlite3 query:
Edit 2:
I fixed it by confirming the cipher was not foreign key linked to any other table then deleting it manually.
The cipher did not show up in the web browser trash folder, and the app trash folder showed it but crashed when i tried to interact with it.
So I pulled a:
I am assuming that if I updated the data column to be valid JSON instead of an empty string, I would have been able to interact with it in the app. But that is just a guess.
@maximilize commented on GitHub (Jul 9, 2026):
That last line is your culprit:
{"atype":5,"data":"","trash":1}— an SSH-key item (type 5) sitting in Trash with an emptydatafield.Trace through
Cipher::to_json(src/db/models/cipher.rs): the emptydatafails to parse, so it falls back to{}(:253-255); the SSH guard then sees the mandatory key fields missing and blanks the whole entry tonull(:300-306), emitted as"sshKey": null(:410). Thatnullis exactly theinvalid type: unit value, expected a valid stringyour 2026.5 client aborts on — so this single item blanks the entire vault. It's the same SSH-in-Trash class as @lennysh/@Kuuzoo, not theEncString(#7333) one.Fix: delete that item from Trash using a client/web-vault that still loads (downgrade to 2026.4.1 as before, empty the Trash, then re-upgrade). #7308 is the proper server-side fix and covers the Trash path.
The
jq: Expected JSON valueerror is just the emptydatabreakingfromjsonin the diagnostic, not a separate problem.