You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Desktop overwrites an undecryptable connection-catalog.json with an empty catalog, permanently deleting saved environments #23470
macOS, T3 Code Nightly with several saved environments (SSH targets and remote profiles).
Have connection-catalog.json encrypted under a safe-storage identity that differs from the one the running build uses. This happened naturally across nightlies: 20261020 used T3 Code (Nightly) Safe Storage, and 20261021 (the [Bug]: Mac Nightly: projects and chats appear missing after this morning's update #22216 fix) returned to t3code Safe Storage. Any user who re-saved the catalog on 20261020 now holds a file under the Nightly key.
Update to the next build and open the desktop.
Expected behavior
If the catalog cannot be decrypted, the desktop leaves the file untouched. It tries other known identities (t3code, T3 Code (Nightly), T3 Code (Alpha), T3 Code (Beta)) and then re-encrypts the file under the current identity. If no identity works, it shows a recoverable error.
Actual behavior
desktop.connectionCatalogStore.get fails with DesktopConnectionCatalogStoreProtectionError (decrypt-catalog). The renderer continues with an empty catalog, and its next desktop:set-connection-catalog call writes {"targets":[],"profiles":[],"credentials":[],...} over the file. The original catalog, including credentials and DPoP tokens, is gone. Remote environments and their threads disappear from the desktop, while the web client still shows them.
In 20261021 the store's get has no previous-identity fallback (the decryptWithPreviousName path seen in 20261020 traces is gone), and set writes unconditionally.
Suggested fix
Never let set replace a catalog that the last get failed to decrypt. Keep the unreadable file (or move it to connection-catalog.json.undecryptable-<ts>) until the user resolves it.
Restore a fallback in get that tries every historical safe-storage identity and migrates on success.
Correction to the failure path above, after reading the source: on the Mac where the catalog was wiped, desktop.connectionCatalogStore.get succeeded at startup and set followed 36 ms later. The empty write comes from the renderer's corrupt-catalog recovery in makeCatalogStore (apps/web/src/connection/storage.ts), which writes EMPTY_CONNECTION_CATALOG_DOCUMENT when the returned catalog does not decode. The desktop backend has no quarantine, so nothing is kept. On the second Mac, get failed with decrypt-catalog and nothing was written.
Proposed fix: #23488 keeps the existing file as connection-catalog.json.unreadable-<ms> before the first write, if it does not decrypt and decode.
Activity
rbansal42 commentedon Oct 22, 2026
Correction to the failure path above, after reading the source: on the Mac where the catalog was wiped,
desktop.connectionCatalogStore.getsucceeded at startup andsetfollowed 36 ms later. The empty write comes from the renderer's corrupt-catalog recovery inmakeCatalogStore(apps/web/src/connection/storage.ts), which writesEMPTY_CONNECTION_CATALOG_DOCUMENTwhen the returned catalog does not decode. The desktop backend has noquarantine, so nothing is kept. On the second Mac,getfailed withdecrypt-catalogand nothing was written.Proposed fix: #23488 keeps the existing file as
connection-catalog.json.unreadable-<ms>before the first write, if it does not decrypt and decode.