fix(accounts): keep the switcher within the keychain's value cap - #157
Merged
Conversation
The quick switcher kept every saved account, and each one's parked UI state, in a single keychain value. Windows Credential Manager refuses a credential blob over 2560 bytes once encoded as UTF-16, which two accounts' tokens exceed on their own — and every call site swallowed the rejection. Adding a second account wrote nothing, so it never joined the switcher; parking the outgoing account's workspace snapshot wrote nothing either, so switching back came up with no tabs at all. The switcher is now one entry per account plus an index of their ids, each well under the cap, and the UI state parks in localStorage, where the same data already lives while the account is signed in. A pre-0.29 single-value list migrates on first read. A failed read is no longer indistinguishable from an empty switcher: it blocks the write that would otherwise persist that emptiness over real accounts. Failures reach the user as a toast, and "Add another account" refuses to sign this one out when it could not be saved first. Restoring the workspace now waits for the replace-mode login sync. A switch wipes the config dir, so reconnecting before the cloud pull refills it finds no connection and errors every restored tab; a normal launch reads its cache from disk and never waits.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported on Windows: after adding a second account through "Add another account…" and switching back, the persistent sessions never reopened — no tabs at all — and the second account was gone from the switcher.
Root cause
Both symptoms come from one place. The switcher kept every saved account, and each one's parked UI state, in a single keychain value. Windows Credential Manager refuses a credential blob over
CRED_MAX_CREDENTIAL_BLOB_SIZE(2560 bytes), checked after UTF-16 encoding, so 1280 ASCII characters. With real tokens one entry is ~940 characters and two are ~1890 — past the cap. Every call site swallowed the rejection.Unit tests never caught it because they fake an unbounded keychain — and on Linux/macOS the Secret Service and Apple keychain have no comparable cap, so it only bites Windows (and headless Linux, where the
linux_keyutilsfallback caps 20 KB per user for everything).Changes
voltius.saved_account.<id>) plus an index of ids. A pre-0.29 single-value list migrates on first read, and leaves the old value alone if a write fails so it can retry.voltius.parked-ui-state.<id>), where the same data already lives while the account is signed in. It runs to kilobytes — workspace snapshot, command history, snippet variables — and never belonged in a keychain value. Signing an account out drops its parked copy.loadSavedAccountsreturns{ ok, accounts };ok: falseblocks the write that used to persist that emptiness over real accounts.layout.sidebarAccount.saveFailed, 4 locales), aconsole.warnon the splash, and "Add another account" aborts rather than signing the account out when it could not be saved first.plugins/runtime.tsintosrc/services/loginSyncGate.ts.Tests
savedAccounts.test.tsnow drives a keychain fake that enforces the real cap (2560 bytes, UTF-16), which the previous layout cannot pass, plus tests for the legacy migration, a refused migration, and the unreadable-keychain case.workspaceRestore.test.tsis new and was confirmed to fail with the gate line removed.Full suite 3582 passing,
tsc --noEmitclean.Not verified here
The trigger is Windows-only, so no run on this machine can reproduce the original failure — this needs an add-account → switch-back on Windows to confirm in the wild.
Known residual, deliberately left: between the localStorage clear and the reload, a late zustand persist write could hand the outgoing account's in-memory state to the incoming one. Pre-existing and narrow; closing it means quiescing the stores.