Skip to content

[Bug]: V2 desktop seeds t3code-v2 Local State from a stale "T3 Code (Alpha)" profile, making connection-catalog.json undecryptable #15001

Open

Description

@J-clone

Area: apps/desktop
Impact: Blocks work completely

Steps to reproduce

Windows machine that has used T3 Code since the Alpha era, so both of these exist:

  • %APPDATA%\T3 Code (Alpha)\Local State (stale, last written months ago)
  • %APPDATA%\t3code\Local State (the profile stable builds actually read the safeStorage key from)
  1. On stable 0.0.45, have at least one saved remote connection so ~/.t3/userdata/connection-catalog.json exists. Everything works.
  2. Install nightly 0.0.46-nightly.20261003.2610 (first Orchestrator V2 build) and launch it.
  3. The app relaunches with --user-data-dir=%APPDATA%\t3code-v2. resolveUserDataPath creates that folder and copies Local State into it from T3 Code (Alpha) because that folder exists, instead of from t3code.
  4. Open the app.

Expected behavior

The V2 profile is seeded with the key that actually encrypted the existing connection catalog (the t3code profile key on this machine), so saved connections load and the local environment registers.

Actual behavior

Every launch fails to decrypt connection-catalog.json. The web client reports "Could not load the local connection catalog", no environment gets registered (the renderer logs " is not connected" for the local machine itself), the sidebar shows no threads, nothing can be sent, and Settings shows "Connect an environment to choose its text generation model."

Switching back to stable 0.0.45 restores everything, because stable reads the key from %APPDATA%\t3code. Switching to the nightly breaks it again.

Evidence

I decrypted the catalog manually with each profile's os_crypt.encrypted_key (DPAPI unwrap, then AES-256-GCM on the v10 blob):

Profile Result
T3 Code (Alpha) fails, GCM auth tag mismatch
t3code decrypts (schemaVersion, targets, profiles, credentials, remoteDpopTokens)
t3code-v2 (seeded by the nightly) fails, identical key to Alpha

Relevant code in the shipped bundle (apps/desktop/dist-electron/main.cjs, resolveUserDataPath):

const legacyState = path.join(legacyPath, "Local State");            // T3 Code (Alpha)
const sourceState = (yield* inspect(legacyState))
  ? legacyState
  : path.join(input.appDataDirectory, "t3code", "Local State");

The Alpha profile takes precedence whenever it exists, regardless of which key the catalog was encrypted with. Note that the nightly also re-encrypts clerk-tokens.json with the new (wrong) key, so simply swapping the key afterwards invalidates that token.

Logs

desktop.trace.ndjson (repeated on every launch, 24 times tonight):

DesktopConnectionCatalogStoreProtectionError: Desktop connection catalog protection failed during decrypt-catalog at C:\Users\<user>\.t3\userdata\connection-catalog.json.
    at ...\resources\app.asar\apps\desktop\dist-electron\main.cjs:107992:201
  [cause]: ElectronSafeStorageDecryptError: Electron safe storage failed to decrypt a string.
    [cause]: Error: Error while decrypting the ciphertext provided to safeStorage.decryptString.

Renderer (otlp spans in server.trace.ndjson):

web.connectionStorage.loadCatalog  ConnectionTransientError: Could not load the local connection catalog: Error: Error invoking remote method 'desktop:get-connection-catalog': DesktopConnectionCatalogStoreProtectionError: ...
EnvironmentRpc.request  EnvironmentRpcUnavailableError: <hostname> is not connected.   (environment.id = the local primary environment)

Stable-era logs from earlier the same day contain zero decrypt-catalog failures.

Version / environment

  • T3 Code 0.0.46-nightly.20261003.2610 (nightly track), broken; 0.0.45 stable works
  • Windows 11 Pro 10.0.26200, x64
  • Claude Code 2.1.288, Codex 0.160.0 (both above the V2 minimums in [IMPORTANT] Moving from T3 Code Orchestrator V1 to V2 #14871)
  • The V2 migration itself succeeded: statev2.sqlite has all 169 threads imported with no last_error

Suggested fix

When seeding t3code-v2\Local State on win32, try each candidate source (t3code, then T3 Code (Alpha)) and pick the one whose key actually decrypts the existing connection-catalog.json; or prefer t3code over the Alpha folder, since the Alpha folder is stale on long-time Windows installs. Independently, a decrypt failure should degrade to an empty catalog rather than leaving the local environment unregistered (see #8341 and the closed #13656).

Workaround

Close the nightly, copy os_crypt.encrypted_key from %APPDATA%\t3code\Local State into %APPDATA%\t3code-v2\Local State, relaunch, sign in again. Deleting connection-catalog.json also works but loses saved connections.

Related

Activity

  1. juliusmarminge commented on Oct 3, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the thorough investigation, @J-clone. Decrypting the catalog with each profile's key made this easy to pin down. We confirmed it on main (43bd667). This is a Windows-only bug in how V2 seeds its profile, and it's still present.

    What V2 does: resolveUserDataPath puts the V2 app in %APPDATA%\t3code-v2. On win32 only, it copies Local State once:

    1. If t3code-v2\Local State already exists, it keeps it.
    2. Otherwise it copies T3 Code (Alpha)\Local State if that file exists.
    3. Otherwise it copies t3code\Local State.

    const names = input.isDevelopment
    ? { current: "t3code-dev", legacy: "T3 Code (Dev)" }
    : { current: "t3code-v2", legacy: "T3 Code (Alpha)" };
    const destinationPath = path.join(input.appDataDirectory, names.current);
    const legacyPath = path.join(input.appDataDirectory, names.legacy);
    const inspect = (resourcePath: string) =>
    fs
    .exists(resourcePath)
    .pipe(
    Effect.mapError((cause) =>
    DesktopUserDataInitializationError.fromFileSystem(cause, "inspect", resourcePath),
    ),
    );
    if (input.isDevelopment) {
    return (yield* inspect(legacyPath)) ? legacyPath : destinationPath;
    }
    // Chromium databases require their own profile for each running version.
    if (input.platform !== "win32") return destinationPath;
    const destinationState = path.join(destinationPath, "Local State");
    if (yield* inspect(destinationState)) return destinationPath;
    const legacyState = path.join(legacyPath, "Local State");
    const sourceState = (yield* inspect(legacyState))
    ? legacyState
    : path.join(input.appDataDirectory, "t3code", "Local State");

    That order matches what you found. On your machine, and in the 2026-10-03 notes on #8341, connection-catalog.json decrypts with the t3code key, and the Alpha key is stale. V2 still prefers Alpha whenever its Local State exists, so the new profile gets a key that can't open the catalog. DesktopUserData.test.ts tests each source on its own but never the case where both files exist. macOS and Linux don't copy Local State at all.

    Why everything stops working: after the bad seed, the outage comes from the existing hard failure tracked in #8341. A safeStorage.decryptString failure becomes DesktopConnectionCatalogStoreProtectionError, the catalog can't load, and the local environment never connects. #13656 would have recovered with an empty catalog, but it was closed without merging, so that recovery stays on #8341.

    Fix direction: preferring t3code over Alpha would fix the installs reported so far, but it would pick the wrong key on any Windows profile that really did encrypt with Alpha. The safer fix is that when connection-catalog.json exists, V2 copies whichever Local State key can actually decrypt it. The open PR #14265 also changes how the Windows encryption profile is chosen, so it's worth checking alongside this.

    Already-affected installs: a fix won't repair profiles that 0.0.46-nightly.20261003.2610 already created, because V2 never re-seeds once t3code-v2\Local State exists. Those installs still need your workaround: copy os_crypt.encrypted_key over from the t3code profile before anything else is written with safe storage, or set the undecryptable catalog aside. Signing in again afterwards is expected, since only Local State is copied. Anything the nightly already re-encrypted inside t3code-v2 won't decrypt after the key is swapped.

    We're keeping this open as a bug, separate from #8341.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 3, 2026
  3. darkyeg commented on Oct 3, 2026

    @darkyeg

    Confirmed the same failure on Windows with 0.0.46-nightly.20261003.2623, commit fed41fa. This also affects the later nightly, not only .2610.

    Read-only checks before recovery:

    • The encrypted connection catalog decrypted with %APPDATA%\t3code\Local State.
    • It failed authentication with both %APPDATA%\T3 Code (Alpha)\Local State and %APPDATA%\t3code-v2\Local State.
    • The V2 encryption key matched Alpha's key, rather than t3code's key.
    • state.sqlite and statev2.sqlite each contained the same 8 non-deleted projects and 42 non-deleted threads. The V2 thread projection also contained 42 threads.
    • Desktop traces showed DesktopConnectionCatalogStoreProtectionError during decrypt-catalog, caused by ElectronSafeStorageDecryptError.

    Recovery verified on this machine:

    1. Backed up the encrypted connection catalog.
    2. Decrypted its existing v10 payload with the old t3code key in memory.
    3. Re-encrypted the identical plaintext with the current t3code-v2 key and a fresh GCM nonce; verified an exact plaintext round-trip before and after writing.
    4. Restarted the desktop app.

    This preserves the V2 key for credentials/cookies that may already have been written with it, and retains saved remote connection credentials rather than deleting the catalog. After restart, desktop.connectionCatalogStore.get and its IPC call both returned Success; environment handshake and project metadata snapshot calls also succeeded. The user confirmed chats appeared again. The saved laptop connection was retained and its environment descriptor returned HTTP 200.

    For a regression test, please cover both legacy profiles coexisting with different keys, with the catalog encrypted using the t3code key. Also cover recovery after an already-created V2 profile has started encrypting other credentials with the Alpha-derived key; reseeding only on first launch will not repair those users.

    No raw logs, tokens, encryption keys, hostnames, workspace paths, or conversation content are attached.

  4. Timmyy3000 commented on Oct 3, 2026

    @Timmyy3000

    Hit this too on Windows 11 after updating to the Nightly channel today (2026-10-03). Desktop trace showed DesktopConnectionCatalogStoreProtectionError … during decrypt-catalog, and the UI showed "local environment is having problem setting up" with no projects or threads. The data was intact in statev2.sqlite (the v2 migration succeeded). Workaround: quit the app, rename ~/.t3/userdata/connection-catalog.json to .bak, relaunch.

  5. kharitonov-egor commented on Oct 4, 2026

    @kharitonov-egor

    I'm hitting this too, on Windows 11 Pro (10.0.26200).

    What happened. I updated from stable to nightly 0.0.46-nightly.20261004.2644. The sidebar showed "No projects yet". Rolling back to 0.0.46-nightly.20261003.2632 didn't help, because that build is V2 as well. Going back to 0.0.45-nightly.20261002.2595, the last nightly before #2829, brought every project and thread back.

    The data was fine. ~/.t3/userdata/statev2.sqlite had a complete migration: 27 rows in projection_projects, 422 in orchestration_v2_projection_threads, 422 in orchestration_v2_legacy_imports. Nothing was lost. The app just couldn't register the local environment.

    Matches this issue. I have all of these under %APPDATA%: T3 Code (Alpha), t3code, t3code-dev, t3code-v2. The desktop trace has 48 DesktopConnectionCatalogStoreProtectionError entries across desktop.trace.ndjson and desktop.trace.ndjson.1, all from V2 launches.

    Confirmed on 2644, so the bug is still present in the Oct 4 nightly as well as 2610 and 2623. For now I'm staying on 2595 until this is fixed.

  6. Sebastian-Neubert commented on Oct 7, 2026

    @Sebastian-Neubert

    Independent confirmation on Windows after upgrading from stable 0.0.45 to 0.0.46-nightly.20261007.2787. This is still reproducible in the October 7 nightly, which was the newest published nightly when checked.

    Read-only diagnosis before recovery:

    • The existing encrypted connection catalog authenticated successfully with the key from %APPDATA%\t3code\Local State.
    • It failed authenticated decryption with both the Alpha and pre-repair V2 profile keys.
    • Desktop logs reported DesktopConnectionCatalogStoreProtectionError during decrypt-catalog, caused by ElectronSafeStorageDecryptError.
    • Database migration had succeeded: all 403 V1 thread IDs were present in V2, all 9,961 original message records were retained in the copied legacy tables, and all 9,245 eligible user/assistant messages matched by ID, thread, role, and text. There were no import errors.

    Recovery was verified in the installed desktop app, without resetting the catalog or recreating either database:

    1. Created consistent SQLite snapshots, preserved the relevant profile/key/catalog files, and verified the backups.
    2. Closed T3 completely and changed only os_crypt.encrypted_key in V2's Local State to the validated working stable-profile value.
    3. Relaunched the same nightly. Existing threads, conversation history, and image attachments rendered. Catalog reads succeeded with no new decryption failures.
    4. Compared settings and saved connections: settings/keybindings files were byte-identical, and all three connection targets and both connection profiles were semantically unchanged. The catalog was subsequently rewritten by the app, so its ciphertext changed normally.

    T3 Connect required a fresh sign-in after the key change, which the user completed successfully. This supports the existing warning about credentials written under the initial V2 key.

    The source-selection order and early return for an existing V2 Local State are still present in current main at 26285ab8. #15072 is already in this nightly, but its Local Storage import does not repair this encryption-key mismatch.

    Useful regression cases would include both legacy profiles coexisting with different keys, a catalog encrypted under either candidate key, and an already-created V2 profile containing credentials encrypted under its initial key. Recovery should retain saved connections and account for V1/V2 sharing the catalog, rather than automatically replacing recoverable data with an empty catalog. Re-encrypting that shared catalog under a different V2 key also needs a V1 compatibility check.

    Investigated with Codex. No keys, tokens, raw logs, conversation content, or identifying paths are included here.

  7. CountMyBands commented on Oct 10, 2026

    @CountMyBands

    Still reproducible on 0.0.46-nightly.20261010.2935 (Windows 11 Pro 10.0.26200, x64), after upgrading from stable through the desktop app.

    • The sidebar showed no threads after the update. %APPDATA% contains T3 Code (Alpha), t3code, t3code-dev, and t3code-v2.
    • desktop.trace.ndjson logs DesktopConnectionCatalogStoreProtectionError … during decrypt-catalog on every nightly launch (28 entries in the current file).
    • The data was intact: statev2.sqlite was seeded from state.sqlite at first launch. Both have 2,114 threads and 36 projects. orchestration_v2_projection_threads and orchestration_v2_legacy_imports each have 2,114 rows, with 0 pending transcripts and 0 import errors.
    • Workaround confirmed: fully quit the app, rename ~/.t3/userdata/connection-catalog.json to .bak, relaunch. All threads came back.
    • Side effect: state.sqlite kept being written by the stable build for ~30 minutes after V2 seeded statev2.sqlite, and the seed never runs again, so that work isn't in V2. Probably worth a separate note if the cutover is meant to be one-way.

    Diagnosed via t3 triage with Claude Code (Claude Opus 5.5).

  8. Stebulous commented on Oct 15, 2026

    @Stebulous

    Note

    Claude (Opus 5.5) posting on behalf of @Stebulous.

    Hit on 0.0.46-nightly.20261014.3132 (Windows 11 Pro 10.0.26200, x64). apps/desktop/src/app/DesktopUserData.ts is byte-identical from 339179f (nightly 3145) through nightly 3176, v0.1.0 and main at d2d59fa, so the bug is still present. Now that 0.1.0 is on stable, any Windows install that still has T3 Code (Alpha)\Local State and no t3code-v2 profile yet takes this path on upgrade.

    • Upgraded from stable 0.0.45 by switching the update track to Nightly. %APPDATA% had both T3 Code (Alpha) (last written March) and t3code (the profile in use since June).
    • After relaunch, the sidebar showed "No projects yet", Settings → Connections showed "No environment selected" with no saved remote environments, and General showed "No providers available".
    • t3code-v2\Local State had the same os_crypt.encrypted_key as T3 Code (Alpha), not t3code. desktop.trace.ndjson logged DesktopConnectionCatalogStoreProtectionError … during decrypt-catalog on each launch.
    • The data was intact. statev2.sqlite had 354 rows in both orchestration_v2_legacy_imports and orchestration_v2_projection_threads, with 0 import errors.

    Workaround that keeps saved connections: with the app fully quit, copy only os_crypt.encrypted_key from %APPDATA%\t3code\Local State into %APPDATA%\t3code-v2\Local State (back up the file first), then relaunch. The catalog decrypted, the local environment connected, and saved remote environments came back. Unlike renaming connection-catalog.json, this keeps them. clerk-tokens.json was rewritten on the next launch with no decrypt failures in the desktop trace.

    Edited 2026-10-16: corrected the build list; 3132 was not the newest nightly.

  9. g1331 commented on Oct 16, 2026

    @g1331

    Independent confirmation on stable 0.1.0: existing V2 profile cannot decrypt the shared connection catalog

    Adding an incident observed after a Windows desktop update to stable 0.1.0 on 2026-10-16. This appears to be the already-affected-profile case discussed above, so I am adding evidence here rather than opening a duplicate.

    Area, environment, and impact

    • Area: apps/desktop.
    • Windows x64, OS build 26300.9711.
    • Installed executable reports product version 0.1.0.0 / file version 0.1.0; the persisted local backend record also reports serverVersion 0.1.0.
    • Both %APPDATA%\t3code and %APPDATA%\t3code-v2 already exist. V2 profile artifacts predate this update.
    • The user reports that projects, saved remote environments, and providers are all missing from the desktop UI, blocking normal use.

    Incident sequence and expected behavior

    1. Use an existing Windows installation with a saved encrypted ~/.t3/userdata/connection-catalog.json and an already-created V2 profile.
    2. Update the desktop app to stable 0.1.0 and launch it.
    3. The local backend reaches readiness, but reading the saved connection catalog fails. Subsequent launches in the same incident show the same failure.

    Expected: the local environment and saved remote entries should load, and existing projects and provider settings should be available.

    This is captured field evidence, not a clean-install reproduction of how the mismatched keys were first created.

    Read-only findings before any recovery

    Authenticated decryption of the same existing catalog was tested in memory using each profile's Windows-protected key: DPAPI unwrap, then AES-GCM authentication of the existing v10 payload.

    Key source Result
    %APPDATA%\t3code\Local State Successful authenticated decryption; the plaintext parses as a connection catalog
    %APPDATA%\t3code-v2\Local State Authenticated decryption fails

    Additional observations:

    • The two profiles have different os_crypt.encrypted_key values. Neither value is included here.
    • %APPDATA%\T3 Code (Alpha)\Local State is absent at the time of inspection. I cannot establish whether an Alpha profile ever existed or why the V2 key originally diverged; this report does not claim to reproduce the original Alpha-selection trigger.
    • The decryptable catalog still contains a saved connection target. No decrypted connection details or credentials were written to disk or attached.
    • The local V1 and V2 databases both retain four non-deleted project records. The V2 thread projection and message projection are populated.
    • settings.json retains enabled Codex and Claude provider instances.
    • Desktop traces record local backend readiness on the affected launches, followed by catalog-decryption failures. This is not simply a backend that never started.

    Minimal redacted diagnostic excerpt

    desktop.connectionCatalogStore.get
    DesktopConnectionCatalogStoreProtectionError:
      Desktop connection catalog protection failed during decrypt-catalog
      at <user-home>/.t3/userdata/connection-catalog.json.
    [cause]: ElectronSafeStorageDecryptError:
      Electron safe storage failed to decrypt a string.
    [cause]: Error:
      Error while decrypting the ciphertext provided to safeStorage.decryptString.
    

    The same failure propagates through the connection-catalog IPC read.

    Scope and recovery status

    The immediate local fault is verified: the shared catalog authenticates with the stable-profile key and fails with the current V2-profile key. The data files and provider settings remain present.

    The v0.1.0 source still returns the existing V2 profile immediately when its Local State exists, so first-launch seeding alone cannot repair this already-created profile:
    DesktopUserData.ts at v0.1.0.

    No recovery has been applied in this incident yet. In particular, neither profile key, the catalog, nor either database has been replaced or deleted. No successful UI recovery is claimed.

    A separate saved remote server is still on V1. Its protocol compatibility after reconnect is a separate issue; it does not explain why the local catalog fails authenticated decryption or why the local environment cannot load.

    Redaction

    No usernames, hostnames, IP addresses, project/repository names or workspace paths, process IDs, environment/thread/tunnel identifiers, account details, pairing codes, tokens, encryption keys, ciphertext, conversation content, screenshots, databases, or raw logs are included. Only the minimal normalized error excerpt and non-identifying diagnostic results are provided.

    Investigation and report preparation used Codex on the affected user's behalf.

  10. ikifar2012 commented on Oct 16, 2026

    @ikifar2012
    Contributor

    Confirmed a related encryption-key mismatch after updating the stable Windows desktop app to 0.1.0, without using nightly builds.

    What happened and reproduction

    1. Use an existing stable Windows desktop installation with local and WSL environments.
    2. Update through the desktop app to 0.1.0 and relaunch.
    3. Threads disappear from the interface; Connections shows "No environment selected" despite Local environment being enabled, General shows "No providers available", and T3 Connect appears signed out.

    This is field evidence; the original cause of the key divergence has not been reproduced in isolation.

    Diagnosis and evidence

    • Windows x64, build 10.0.26300; affected desktop/server version 0.1.0. Triage CLI: 0.1.0 (npx t3 triage).
    • %APPDATA%\t3code\Local State and %APPDATA%\t3code-v2\Local State contained different encryption keys. The V2 file was created at the update.
    • %APPDATA%\T3 Code (Alpha)\Local State was absent at inspection, so this does not establish the stale-Alpha selection trigger described in this issue.
    • Read-only DPAPI unwrap and AES-GCM authentication of the existing catalog succeeded with the old stable profile key and failed with the V2 key.
    • The local database retained 16 threads and 426 messages, including populated V2 projections.

    Redacted error chain:

    DesktopConnectionCatalogStoreProtectionError: protection failed during decrypt-catalog
      at <user-home>/.t3/userdata/connection-catalog.json
    ElectronSafeStorageDecryptError: Electron safe storage failed to decrypt a string.
    Error while decrypting the ciphertext provided to safeStorage.decryptString.
    

    Recovery verification

    With the app fully closed, backed up the V2 Local State file and replaced only os_crypt.encrypted_key with the verified old stable profile key. Authenticated catalog decryption then succeeded with the V2 profile. After relaunch, desktop and renderer catalog reads succeeded, local and WSL backends reached readiness, and environment RPC and initial synchronization succeeded. Visual confirmation of restored threads and T3 Connect sign-in is still pending.

    The exact migration mechanism remains unresolved. Related: #8341. The v0.1.0 source retains an existing V2 Local State without checking whether its key can decrypt the shared catalog: DesktopUserData.ts.

    Prepared with GPT-6 in Codex via t3 triage on the affected user's behalf.

  11. Maxborland commented on Oct 16, 2026

    @Maxborland

    Confirmed on the stable 0.1.0 release (Windows 11, updated from the previous stable through the updater), so this now reaches stable users, not only nightly.

    Same picture as the report: t3code-v2\Local State was seeded with the stale T3 Code (Alpha) key, connection-catalog.json only decrypts with the t3code profile key, and the app looks like a fresh install ("No projects yet", Settings → Providers shows "Connect an environment to set up its providers."). The server side was fine: statev2.sqlite had every legacy thread imported (orchestration_v2_legacy_imports rows with no last_error), so no data was lost.

    Two details that may help:

    • The catalog was empty (targets, profiles, credentials, remoteDpopTokens, disabledEnvironmentIds all had 0 entries). So you don't need saved remote connections to hit this: an existing but undecryptable catalog blocks the local environment by itself ([Bug]: Orphaned safeStorage key makes connection-catalog.json permanently undecryptable, blocking all target listing on Windows #8341).
    • Workaround that keeps sign-in intact: instead of copying the t3code key into t3code-v2\Local State (which invalidates clerk-tokens.json and anything else V2 already encrypted), re-encrypt the catalog. Decrypt it with whichever old profile key works, encrypt it again with the t3code-v2 key, and write it back. After a restart, everything loaded and sign-in still worked.
    PowerShell 7 script (dry run by default, -Apply writes, keeps a backup, prints no secrets)
    param([switch]$Apply)
    # Re-encrypts ~/.t3/userdata/connection-catalog.json with the t3code-v2 safeStorage key.
    # Close T3 Code fully before running with -Apply.
    $ErrorActionPreference = 'Stop'
    Add-Type -AssemblyName System.Security
    
    function Get-Key($dir) {
      $ls = Get-Content (Join-Path $env:APPDATA "$dir\Local State") -Raw | ConvertFrom-Json
      $blob = [Convert]::FromBase64String($ls.os_crypt.encrypted_key)
      $blob = $blob[5..($blob.Length - 1)]  # strip "DPAPI"
      [System.Security.Cryptography.ProtectedData]::Unprotect($blob, $null, 'CurrentUser')
    }
    function Unseal($key, [byte[]]$data) {
      $nonce = $data[3..14]; $tag = $data[($data.Length - 16)..($data.Length - 1)]
      $ct = [byte[]]$data[15..($data.Length - 17)]; $pt = New-Object byte[] $ct.Length
      $g = [System.Security.Cryptography.AesGcm]::new([byte[]]$key, 16)
      try { $g.Decrypt([byte[]]$nonce, $ct, [byte[]]$tag, $pt); $pt } catch { $null } finally { $g.Dispose() }
    }
    function Seal($key, [byte[]]$pt) {
      $nonce = [System.Security.Cryptography.RandomNumberGenerator]::GetBytes(12)
      $ct = New-Object byte[] $pt.Length; $tag = New-Object byte[] 16
      $g = [System.Security.Cryptography.AesGcm]::new([byte[]]$key, 16)
      $g.Encrypt($nonce, $pt, $ct, $tag); $g.Dispose()
      [byte[]]([Text.Encoding]::ASCII.GetBytes('v10') + $nonce + $ct + $tag)
    }
    
    $file = Join-Path $env:USERPROFILE '.t3\userdata\connection-catalog.json'
    $doc = Get-Content $file -Raw | ConvertFrom-Json
    $data = [Convert]::FromBase64String($doc.encryptedCatalog)
    $newKey = Get-Key 't3code-v2'
    
    if (Unseal $newKey $data) { 'Already readable with the new key, nothing to do.'; return }
    $pt = $null
    foreach ($d in 'T3 Code (Alpha)', 't3code') { $pt = Unseal (Get-Key $d) $data; if ($pt) { "Decrypted with key from: $d"; break } }
    if (-not $pt) { throw 'No known key decrypts the catalog.' }
    
    $sealed = Seal $newKey $pt
    if ((Compare-Object (Unseal $newKey $sealed) $pt)) { throw 'Round-trip check failed.' }
    'Round-trip with new key: OK'
    
    if ($Apply) {
      Copy-Item $file "$file.backup"
      $doc.encryptedCatalog = [Convert]::ToBase64String($sealed)
      [IO.File]::WriteAllText($file, ($doc | ConvertTo-Json -Compress))
      "Written. Backup: $file.backup"
    } else { 'Dry run. Re-run with -Apply to write.' }
  12. realS3BI commented on Oct 16, 2026

    @realS3BI

    Confirmed on stable 0.1.0 (Windows), after updating through the desktop app. Same symptoms as above: "No projects yet", no environment, no providers.

    The key-copy workaround fixed it for me. Fully quit T3 Code (including the tray icon), then in PowerShell:

    $src = "$env:APPDATA\t3code\Local State"
    $dst = "$env:APPDATA\t3code-v2\Local State"
    
    # Backup
    Copy-Item $dst "$dst.bak"
    
    # Copy only os_crypt.encrypted_key from the t3code profile into t3code-v2
    $key = (Get-Content $src -Raw | ConvertFrom-Json).os_crypt.encrypted_key
    $txt = Get-Content $dst -Raw
    $new = [regex]::Replace($txt, '("encrypted_key"\s*:\s*")[^"]*(")', { param($m) $m.Groups[1].Value + $key + $m.Groups[2].Value })
    [IO.File]::WriteAllText($dst, $new)

    After relaunching, projects, threads and saved connections were back. I had to sign in to T3 Connect again, as others here mentioned.

    To revert: Copy-Item "$env:APPDATA\t3code-v2\Local State.bak" "$env:APPDATA\t3code-v2\Local State" -Force

  13. john3374 commented on Oct 16, 2026

    @john3374

    Another instance on stable 0.1.0, Windows 11 Pro 10.0.26200, reached through the normal in-app auto-update from 0.0.45 (not nightly). Every launch logged DesktopConnectionCatalogStoreProtectionError ... decrypt-catalog, the web client reported "Could not load the local connection catalog", and the sidebar was empty. Rolling back to 0.0.45 restored everything.

    Key test (DPAPI unwrap of each profile's os_crypt.encrypted_key, then AES-256-GCM on the v10 blob):

    Profile Opens the catalog
    t3code yes (2 relay targets, 2 DPoP tokens)
    T3 Code (Alpha) no
    t3code-v2 no, key identical to Alpha

    t3code-v2\Local State had been seeded from the Alpha profile on 2026-10-03 by a V2 nightly, matching the triage above.

    Recovery that preserved the saved connections and the sign-in: with the app quit, decrypt the catalog with the t3code key and re-encrypt the identical plaintext with the t3code-v2 key (fresh nonce), keeping a backup. After relaunch desktop.connectionCatalogStore.get succeeded and both relay connections were restored. Note the web client briefly dropped the relay environments at startup via EnvironmentRegistry.removeRelayEnvironments before the Clerk session finished loading, then re-registered them about a minute later.

    Same machine also hit #15017 (stale statev2.sqlite snapshot) because of the rollback.

    Filed by Claude Fable 5.1 via t3 triage.

  14. claudioscatoli commented on Oct 22, 2026

    @claudioscatoli

    Confirmed on stable 0.1.1 and 0.1.2-nightly.20261022.3512 (Windows 11 Pro 10.0.26100, x64, desktop app with its local server). So this bug now reaches stable users who update from 0.0.x.

    Read-only diagnosis:

    • %APPDATA% contains T3 Code (Alpha), t3code and t3code-v2. t3code-v2\Local State was seeded on 2026-10-16, and its os_crypt.encrypted_key is identical to Alpha's, not to t3code's.
    • connection-catalog.json (v10, last written 2026-10-20 by a pre-0.1.0 build) decrypts with the t3code key. It fails with both the Alpha key and the t3code-v2 key.
    • Desktop traces: the last connectionCatalogStore.get success was on 2026-10-20 (pre-0.1.0 build). Every get from the first 0.1.1 launch on 2026-10-22 fails with DesktopConnectionCatalogStoreProtectionError (decrypt-catalog) → ElectronSafeStorageDecryptError.
    • Data is intact: pre-0.1.0 desktop builds still show everything, and t3 CLI + web app show all projects and threads.
    • DesktopUserData.ts has not changed on main since 2026-10-16, so this is still unfixed.

    Given #23470, an affected install can also lose the undecryptable catalog to an empty overwrite. That makes this more urgent now that stable users are hitting it.

    Workaround: copy os_crypt.encrypted_key from t3code\Local State into t3code-v2\Local State with the app closed, after backing up both files.
    Verified: after the swap, 0.1.1 reads the original catalog (connectionCatalogStore.get → Success) and all environments are back.

    Diagnosed with t3 triage by Claude Opus 5.5 (claude-opus-5-5) in Claude Code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions