Skip to content

[Bug]: Mac Nightly: projects and chats appear missing after this morning's update #22216

Closed

Description

@NatanSlvdr

Before submitting

  • I searched existing issues and did not find a duplicate of this Nightly update failure. Related issues are listed below.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/desktop

Steps to reproduce

Observed on an existing Mac desktop installation; a clean-profile reproduction has not been established:

  1. Have existing projects and chat history in T3 Code.
  2. Update the Mac desktop app this morning, 20 October 2026.
  3. Reopen the app.

Expected behavior

Existing projects and chats remain accessible after an update.

Actual behavior

All my projects and chats appeared to be missing after the update, making it look as though I had lost all my data.

Investigation identified a connection-catalog decryption failure. The history was still present in the database; the app could not load access to it.

This is a Nightly report. #20906 describes a related encryption-identity problem, but explicitly excludes Nightly and is now closed as fixed. Please check whether this is a follow-up regression from that fix, rather than assuming its original channel scope still holds.

Impact

Blocks work completely

Version or commit

T3 Code Nightly. The exact build is omitted from this public report.

Environment

Mac desktop app.

Logs or stack traces

Generic error, without local paths or full logs:

DesktopConnectionCatalogStoreProtectionError:
Desktop connection catalog protection failed during decrypt-catalog.

Caused by:
ElectronSafeStorageDecryptError:
Electron safe storage failed to decrypt a string.

The exact encryption-key mismatch has not been established.

Screenshots, recordings, or supporting files

No private files or full logs attached.

Workaround

Access was restored locally after preserving the failing catalog and resetting the catalog state. This is a workaround, not a confirmed fix for the underlying decryption failure.

Related: #20906 and #20907 (encryption-identity change), #8341 and #15001 (similar Windows failures).

Activity

  1. t3dotgg commented on Oct 20, 2026

    @t3dotgg
    Member

    Actively on it - so sorry!

  2. titipakorn-th commented on Oct 20, 2026

    @titipakorn-th

    Revert back to a previous version for now.

  3. deondejongh commented on Oct 20, 2026

    @deondejongh

    Second Mac Nightly install with the same failure. I think this pins down the key mismatch.

    Diagnosis

    • Before today's update, this Mac's Nightly safeStorage key was the Keychain item t3code Safe Storage (account t3code Key).
    • fix(desktop): saved connections survive the Beta rename on macOS and Linux #20907 (3819eb3) now calls app.setName(resolveDesktopSafeStorageName(...)) before ready. For nightly builds that returns T3 Code (Nightly).
    • On the first launch of the new build, Electron created a new Keychain item, T3 Code (Nightly) Safe Storage, at 08:12:24Z. The first decrypt-catalog failure is in the same second.
    • decryptWithPreviousName only runs when the current name is T3 Code (Alpha), and it only tries the T3 Code (Beta) key. Nightly has no fallback, so the old t3code key is never tried.
    • [Bug]: Saved connections no longer decrypt on Linux and macOS after the Alpha → Beta rename #20906 assumed Nightly was safe because it "has always been packaged as T3 Code (Nightly)". On this Mac, no T3 Code (Nightly) Safe Storage item existed before today. The only other T3 item is t3code Safe Storage, so that is very likely the key the old catalog uses. I have not yet confirmed this by decrypting with it.

    Suggested fix

    On macOS/Linux nightly, when the catalog fails to decrypt, also try the t3code Safe Storage / t3code Key key, then re-encrypt with the new key, the same way the Beta fallback works.

    Version

    Last good: 0.1.1-nightly.20261020.3386. First bad: 0.1.1-nightly.20261020.3399.

    Environment

    macOS (Darwin 27.0.0), Apple Silicon, desktop app with its local server.

    Evidence

    Keychain (names and creation times only):
      t3code Safe Storage            / t3code Key             created 2026-06-25
      T3 Code (Nightly) Safe Storage / T3 Code (Nightly) Key  created 2026-10-20T08:12:24Z
    
    desktop.trace.ndjson: first decrypt-catalog failure at 2026-10-20T08:12:24Z, none in older rotated logs.
    connection-catalog.json was last written at 07:40Z by build 3386. Its ciphertext starts with "v10".
    
    DesktopConnectionCatalogStoreProtectionError: Desktop connection catalog protection failed during decrypt-catalog at ~/.t3/userdata/connection-catalog.json.
        at desktop.connectionCatalogStore.recoverPreviousIdentityCatalog
      [cause]: ElectronSafeStorageDecryptError: Electron safe storage failed to decrypt a string.
        [cause]: Error: Error while decrypting the ciphertext provided to safeStorage.decryptString.
    

    Workaround

    Not run yet: quit the app, decrypt the catalog with the t3code Safe Storage key, and re-encrypt it with the T3 Code (Nightly) Safe Storage key. I'll report back if it works.

    Filed by: Claude Code (claude-opus-5-5), following the t3 triage playbook.

  4. t3dotgg commented on Oct 20, 2026

    @t3dotgg
    Member

    Warning

    Update: #20907 was reverted in #22219. The next Nightly build goes back to the old t3code key.

    • If you have not run the script yet, do not run it. Wait for the next Nightly build.
    • Do not run this script after you update to that build. It would break your saved connections again.
    • If you ran the script and then updated: quit T3 Code (Nightly) and sign in to T3 Connect again first if needed. Then copy the backup the script made (connection-catalog.json.bak-<number> in ~/.t3/userdata) back over connection-catalog.json. That backup uses the old key.
    • Everyone who launched build .3399 must sign in to T3 Connect again: the Clerk session (clerk-tokens.json) uses the same key and is lost.

    Confirmed on a second Mac (desktop, Nightly 0.1.1-nightly.20261020.3399, macOS arm64). This is a regression from #20907. The data is not lost: the local server and database are fine. Only the saved connection catalog is unreadable, so no local or remote environment loads.

    Cause

    Workaround (macOS)

    This script decrypts the catalog with the old key and encrypts it again with the new Nightly key. It makes a backup first and changes nothing unless an old key works. It needs no installs: it uses the app's bundled Electron as Node and the built-in security tool.

    1. Quit T3 Code (Nightly) with Cmd+Q.
    2. Save the script below as fix-t3-nightly.sh. Do not paste it directly into your shell.
    3. Run bash fix-t3-nightly.sh.
    4. Click Allow on each Keychain prompt.
    5. Open T3 Code (Nightly) again.
    fix-t3-nightly.sh
    #!/bin/bash
    # Restores saved connections (local projects + remotes) after the 2026-10-20
    # T3 Code Nightly update on macOS. See https://github.com/pingdotgg/t3code/issues/22216
    #
    # The update moved the encryption key to a new Keychain item, so the app can no
    # longer read connection-catalog.json. This script decrypts the file with the
    # old key and encrypts it again with the new one. It makes a backup first.
    set -euo pipefail
    
    BIN="/Applications/T3 Code (Nightly).app/Contents/MacOS/T3 Code (Nightly)"
    CATALOG="${CATALOG:-$HOME/.t3/userdata/connection-catalog.json}"
    NEW_NAME="T3 Code (Nightly)"
    OLD_NAMES=("t3code" "t3code-v2" "Electron")
    
    has_key() { security find-generic-password -s "$1 Safe Storage" -a "$1 Key" >/dev/null 2>&1; }
    read_key() { security find-generic-password -s "$1 Safe Storage" -a "$1 Key" -w; }
    
    [ "$(uname)" = Darwin ] || { echo "This fix is for macOS only."; exit 1; }
    [ -x "$BIN" ] || { echo "T3 Code (Nightly) is not in /Applications."; exit 1; }
    [ -f "$CATALOG" ] || { echo "No saved connections file at $CATALOG. Nothing to fix."; exit 0; }
    if pgrep -xq "T3 Code (Nightly)"; then
      echo "Quit T3 Code (Nightly) first (Cmd+Q), then run this again."; exit 1
    fi
    has_key "$NEW_NAME" || { echo "Open T3 Code (Nightly) once, quit it, then run this again."; exit 1; }
    
    # Chromium safeStorage on macOS: "v10" + AES-128-CBC, PBKDF2-SHA1("saltysalt", 1003), IV of spaces.
    # Exit codes: 0 = fixed, 2 = already readable, 3 = OLD_KEY is not the right key.
    JS='
    const fs = require("fs"), crypto = require("crypto");
    const file = process.env.CATALOG;
    const doc = JSON.parse(fs.readFileSync(file, "utf8"));
    const blob = Buffer.from(doc.encryptedCatalog, "base64");
    const iv = Buffer.alloc(16, 0x20);
    const keyFor = (pw) => crypto.pbkdf2Sync(pw, "saltysalt", 1003, 16, "sha1");
    const decrypt = (pw) => {
      if (!pw || blob.subarray(0, 3).toString("latin1") !== "v10") return null;
      try {
        const d = crypto.createDecipheriv("aes-128-cbc", keyFor(pw), iv);
        const text = Buffer.concat([d.update(blob.subarray(3)), d.final()]).toString("utf8");
        return typeof JSON.parse(text) === "object" ? text : null;
      } catch { return null; }
    };
    if (decrypt(process.env.NEW_KEY) !== null) process.exit(2);
    const text = decrypt(process.env.OLD_KEY);
    if (text === null) process.exit(3);
    const c = crypto.createCipheriv("aes-128-cbc", keyFor(process.env.NEW_KEY), iv);
    const out = Buffer.concat([Buffer.from("v10"), c.update(text, "utf8"), c.final()]);
    const backup = `${file}.bak-${Date.now()}`;
    fs.copyFileSync(file, backup);
    const tmp = `${file}.tmp-${process.pid}`;
    fs.writeFileSync(tmp, JSON.stringify({ version: 1, encryptedCatalog: out.toString("base64") }) + "\n", { mode: fs.statSync(file).mode });
    fs.renameSync(tmp, file);
    console.log(`Backup: ${backup}`);
    '
    
    run_node() {
      CATALOG="$CATALOG" NEW_KEY="$NEW_KEY" OLD_KEY="$1" ELECTRON_RUN_AS_NODE=1 \
        "$BIN" -e "$JS" 2> >(grep -v "Node.js environment variables are disabled" >&2)
    }
    
    echo "macOS will ask for Keychain access. Click Allow (not Always Allow)."
    NEW_KEY="$(read_key "$NEW_NAME")"
    
    rc=0; run_node "" || rc=$?
    [ "$rc" = 2 ] && { echo "Your saved connections are already readable. Nothing to fix."; exit 0; }
    
    for name in "${OLD_NAMES[@]}"; do
      has_key "$name" || continue
      echo "Trying the \"$name\" key..."
      rc=0; run_node "$(read_key "$name")" || rc=$?
      if [ "$rc" = 0 ]; then echo "Fixed. Open T3 Code (Nightly) again."; exit 0; fi
      [ "$rc" = 3 ] || { echo "Unexpected error (code $rc). Your file was not changed."; exit 1; }
    done
    
    echo "No old key could read your saved connections. Your file was not changed."
    echo "Please comment on https://github.com/pingdotgg/t3code/issues/22216"
    exit 1

    Linux is not covered by this script. A fix PR is in progress.

    Diagnosed with t3 triage by Claude Opus 5.5 in Claude Code.

  5. hkarlsen06 commented on Oct 20, 2026

    @hkarlsen06

    I also noticed this. Got scared for a little second, before bunx t3 triage reassured me everything would be fine;)

  6. Nsttt commented on Oct 20, 2026

    @Nsttt

    Thanks @t3dotgg, confirmed that fixes it.

  7. juliusmarminge commented on Oct 20, 2026

    @juliusmarminge
    Member
  8. tears-mysthrala commented on Oct 20, 2026

    @tears-mysthrala

    Confirmed the same regression on Linux x64 (Arch, kernel 7.2.11-arch1-1), using the desktop AppImage and its local server.

    Reproduction: From an existing Nightly installation with projects and saved environments, I used the update button in the desktop app. The affected server reported 0.1.1-nightly.20261020.3399. After the update, the desktop showed no projects or other environments. Signing in to T3 Connect again did not restore them.

    Local evidence (redacted):

    • desktop.trace.ndjson recorded DesktopConnectionCatalogStoreProtectionError during decrypt-catalog in desktop.connectionCatalogStore.get and desktop.connectionCatalogStore.recoverPreviousIdentityCatalog at approximately 08:28 and 08:31 UTC on 20 October. The catalog IPC read failed too.
    • The local server still answered /.well-known/t3/environment and reported build .3399 before rollback.
    • The current SQLite database retained 8 non-deleted projects and 9 non-deleted threads. No database repair was needed.
    • The .3399 source only tries previous-name recovery for T3 Code (Alpha) and the Beta key; it has no Nightly fallback. This is consistent with the encryption-identity regression diagnosed above. I did not inspect or test the Linux keyring, so the exact key mismatch on this machine is inferred from the code and failure, not independently proven.

    Recovery verified: I backed up the existing AppImage and encrypted connection catalog, installed the official .3386 x86_64 AppImage after SHA-256 verification, and restarted the desktop app. The server then reported 0.1.1-nightly.20261020.3386; four post-restart catalog reads succeeded with zero failures, and the user confirmed that projects and environments reappeared. The catalog and database were not edited directly.

    This adds a Linux reproduction to the Mac reports. A regression test should cover an existing Linux Nightly catalog across the .3386 → .3399 identity change and verify that local and saved remote environments remain visible after update.

    Investigated with t3 triage by OpenAI Codex (GPT-6).

  9. q1 commented on Oct 20, 2026

    @q1

    insane response time, fixes it for me too

  10. rgrvlsk commented on Oct 20, 2026

    @rgrvlsk
    Contributor

    Same failure here on macOS arm64 (Darwin 27.0), updating Nightly 0.1.1-nightly.20261019.3373 → 0.1.1-nightly.20261020.3399. I can pin down the key mismatch:

    Login Keychain before and after (metadata only):

    • t3code Safe Storage / account t3code Key: created 2026-08-16. This is the key the catalog was encrypted with.
    • T3 Code (Nightly) Safe Storage / account T3 Code (Nightly) Key: created 2026-10-20 08:13:20Z, the first launch of 3399.

    Why: the packaged nightly's app.asar/package.json has "name": "t3code" and no productName. Before #20907, Chromium's OSCrypt therefore initialised under the name t3code. In 3399, DesktopPreReadyPlatform pins resolveDesktopSafeStorageName() = "T3 Code (Nightly)", which creates a fresh key. DesktopSafeStorageIdentity.decryptWithPreviousName returns None unless the pinned name is "T3 Code (Alpha)" (apps/desktop/src/app/DesktopSafeStorageIdentity.ts:29-33). Even then it only tries T3 Code (Beta) Safe Storage, so nightly installs whose catalog sits under t3code Safe Storage get no fallback.

    Trace (desktop.trace.ndjson, on every launch of 3399):

    desktop.safeStorageIdentity.decryptWithPreviousName  Success (None)
    desktop.connectionCatalogStore.recoverPreviousIdentityCatalog  Failure
      DesktopConnectionCatalogStoreProtectionError: … decrypt-catalog
      ElectronSafeStorageDecryptError → Error while decrypting the ciphertext provided to safeStorage.decryptString.
    web.connectionStorage.loadCatalog  ConnectionTransientError
    

    The database was intact (about 1.8k threads); only the catalog was unreadable. Recovery left connection-catalog.json untouched (still written by 3373), so a fixed build can still recover it.

    Suggested fix direction: have the fallback try t3code Safe Storage (and probably the old app.getName() value generally) for every channel, not just Alpha → Beta. Stable installs whose packaged name was also t3code may have the same exposure. I haven't verified that.

    Workaround that worked: reinstalling 0.1.1-nightly.20261019.3373 from Releases. It decrypts the untouched catalog and accepts the 65/66 migrations with only a divergence warning.

    Diagnosed via t3 triage with Claude Opus 5.5 in Claude Code.

  11. alexaka1 commented on Oct 20, 2026

    @alexaka1

    The new nightly restored everything for me.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions