Skip to content

T3 Code Desktop 0.0.23 fails SSH environment pairing due to DateTime schema mismatch #2665

Description

@fionn77

Summary

T3 Code Desktop 0.0.23 on Windows fails when adding an SSH environment because the desktop app decodes JSON API timestamp strings with
Schema.DateTimeUtc instead of Schema.DateTimeUtcFromString.

This causes SSH bearer session bootstrap to fail even though the remote T3 server returns HTTP 200 with a valid JSON response.

Environment

  • App: T3 Code (Alpha)
  • Version: 0.0.23
  • OS: Windows
  • Flow: Add SSH environment

Error

Error invoking remote method 'desktop:bootstrap-ssh-bearer-session':
DesktopSshRemoteApiError: SSH remote API request failed during bootstrap-bearer-session

Desktop logs include:

SchemaError: Expected DateTime.Utc, got "2026-06-11T19:02:03.848Z"
    at ["expiresAt"]

### Remote API response

The remote server returns HTTP 200 for:

/api/auth/bootstrap/bearer

With JSON similar to:

{
  "authenticated": true,
  "role": "client",
  "sessionMethod": "bearer-session-token",
  "expiresAt": "2026-06-11T19:03:20.007Z",
  "sessionToken": "..."
}

### Root Cause

The desktop bundle defines auth response schemas using Schema.DateTimeUtc for fields that are decoded from HTTP JSON responses.

Because JSON timestamps arrive as strings, these fields should use Schema.DateTimeUtcFromString.

In the bundled desktop file:

apps/desktop/dist-electron/main.cjs

The affected schema included:

const AuthBearerBootstrapResult = Schema.Struct({
  authenticated: Schema.Literal(true),
  role: AuthSessionRole,
  sessionMethod: Schema.Literal("bearer-session-token"),
  expiresAt: Schema.DateTimeUtc,
  sessionToken: TrimmedNonEmptyString
});

This rejects the valid JSON string returned by the server.

### Expected Behavior

SSH environment pairing should succeed when the remote server returns a valid ISO timestamp string in expiresAt.

### Actual Behavior

Pairing fails during bearer session bootstrap because the desktop app expects an already-materialized DateTime.Utc value instead of a
JSON string.

### Local Workaround Verified

I patched the installed Windows app.asar locally and changed the relevant JSON response timestamp fields from:

Schema.DateTimeUtc

to:

Schema.DateTimeUtcFromString

After patching, SSH environment pairing worked.

The fields patched locally were:

- AuthBootstrapResult.expiresAt
- AuthBearerBootstrapResult.expiresAt
- AuthWebSocketTokenResult.expiresAt
- AuthPairingCredentialResult.expiresAt
- AuthPairingLink.createdAt
- AuthPairingLink.expiresAt
- AuthClientSession.issuedAt
- AuthClientSession.expiresAt
- AuthClientSession.lastConnectedAt
- AuthSessionState.expiresAt

### Suggested Fix

For schemas used to decode HTTP JSON API responses, replace timestamp fields using:

Schema.DateTimeUtc

with:

Schema.DateTimeUtcFromString

This should align the desktop decoder with normal JSON API behavior, where dates are serialized as ISO strings.

Activity

  1. gthay commented on May 14, 2026

    @gthay

    I can reproduce the same issue on macOS with T3 Code Desktop 0.0.23.

    Environment:

    • App: T3 Code (Alpha)
    • Version: 0.0.23
    • OS: macOS
    • Remote host: Ubuntu VPS
    • Remote T3 server: t3 v0.0.23
    • Flow: Settings -> Connections -> Remote Environments -> Add environment -> SSH launch

    Error:

    Error invoking remote method 'desktop:bootstrap-ssh-bearer-session':
    DesktopSshRemoteApiError: SSH remote API request failed during bootstrap-bearer-session.
    
    Desktop trace shows the same schema failure:
    
    SchemaError: Expected DateTime.Utc, got "2026-06-13T02:48:40.149Z"
    at ["expiresAt"]
    
    Important detail: SSH and the tunnel were working. The remote server received the request through the local forwarded port and returned HTTP 200 for:
    
    POST /api/auth/bootstrap/bearer
    
    Remote trace showed:
    
    http.response.status_code: 200
    http.response.header.content-type: application/json
    
    The response shape was:
    
    {
      "authenticated": true,
      "role": "client",
      "sessionMethod": "bearer-session-token",
      "expiresAt": "2026-06-13T02:48:40.149Z",
      "sessionToken": "..."
    }
    
    So this is not an SSH auth/tunnel failure. It is the desktop app rejecting a valid JSON response because expiresAt is decoded with Schema.DateTimeUtc instead of a JSON-string decoder like Schema.DateTimeUtcFromString.
    
    I also verified a workaround: running the remote T3 server manually on the VPS loopback interface and using a normal SSH LocalForward plus pairing URL works, because it avoids the desktop-managed SSH bearer bootstrap path.
    
  2. ardenworks commented on May 14, 2026

    @ardenworks

    Opened a small PR for this: #2694

    The issue is a schema/wire-format mismatch: auth timestamps arrive as ISO strings over HTTP JSON, but the desktop decoder expected DateTimeUtc. The PR switches those auth response fields to DateTimeUtcFromString.

    Tested locally, and SSH pairing works now.

  3. dimasmjunior commented on May 23, 2026

    @dimasmjunior

    I can confirm this still reproduces with T3 Code Desktop / CLI 0.0.24 on Fedora 43 using an SSH remote environment.

    Setup:

    • Workstation: Fedora 43, T3 Code Desktop installed from the 0.0.24 AppImage
    • Remote VM: Fedora 43, t3 v0.0.24 installed via npm, launched by the desktop SSH remote flow
    • Remote server: managed by desktop on 127.0.0.1:3773

    Desktop error:

    Error invoking remote method 'desktop:bootstrap-ssh-bearer-session': DesktopSshRemoteApiError: SSH remote API request failed during bootstrap-bearer-session.
    (local http://127.0.0.1:<port>/, remote port 3773, remote server managed)
    

    The SSH tunnel and remote API were actually working. The desktop trace showed the real failure while decoding the successful bearer bootstrap response:

    SchemaError: Expected DateTime.Utc, got "2026-06-20T17:27:37.815Z"
    at ["expiresAt"]
    

    After patching the remote bundle to remove fractional seconds, it still failed with:

    SchemaError: Expected DateTime.Utc, got "2026-06-20T17:33:06Z"
    at ["expiresAt"]
    

    As a local workaround, I patched the remote t3 bundle to serialize expiresAt as Effect DateTime JSON instead of an ISO string:

    const toDateTimeUtcJson = (value) => ({
      "~effect/time/DateTime": "~effect/time/DateTime",
      _tag: "Utc",
      epochMilliseconds: DateTime.toEpochMillis(DateTime.toUtc(value)),
    });

    and used that for the bearer bootstrap response. After restarting the managed remote server, adding the SSH remote worked.

    That matches the diagnosis here: the desktop client is decoding HTTP JSON response fields with Schema.DateTimeUtc even though the remote server sends normal JSON ISO date strings. The proper fix seems to be the one in #2694: decode those HTTP response timestamps with Schema.DateTimeUtcFromString.

  4. jrizo0 commented on May 26, 2026

    @jrizo0

    Confirming this still reproduces on 0.0.24 (latest npm latest tag).

    Environment

    • Desktop: T3 Code (Alpha) 0.0.24 on macOS
    • Remote: Ubuntu 24.04 VPS, Node v24.15.0 (from NodeSource), t3@0.0.24 installed via npx
    • Flow: Option 3 — Desktop-Managed SSH Launch

    Symptom (identical to the original report)
    The remote server starts cleanly, runs all 30 migrations, listens on 127.0.0.1:3773, and prints a valid pairing URL. The desktop opens the SSH forward, issues a pairing token via t3 auth pairing create --json, and POSTs {"credential":"<token>"} to /api/auth/bootstrap/bearer. The server responds HTTP 200 with a well-formed payload:

    {
      "authenticated": true,
      "role": "client",
      "sessionMethod": "bearer-session-token",
      "expiresAt": "2026-06-25T16:27:53.081Z",
      "sessionToken": "eyJ..."
    }

    The desktop then fails to decode the response. From ~/.t3/userdata/logs/desktop.trace.ndjson:

    SchemaError: Expected DateTime.Utc, got "2026-06-25T16:27:53.081Z"
        at ["expiresAt"]
    

    The error surfaces in the UI as:

    Error invoking remote method 'desktop:bootstrap-ssh-bearer-session':
    DesktopSshRemoteApiError: SSH remote API request failed during bootstrap-bearer-session.
    

    Why other flows are not affected
    The strict decode is unique to the SSH path. DesktopSshRemoteApi.bootstrapBearerSession runs the JSON body through Schema.decodeUnknownEffect(AuthBearerBootstrapResult), while the LAN / Tailscale / Remote pairing path (apps/web/src/environments/remote/api.ts) does return (await response.json()) as T; and skips schema validation, so the ISO string for expiresAt slips through unchecked. That is why this bug only manifests through Option 3.

    Verified workaround (no patching required)
    Skip Option 3 and use Option 2 instead:

    1. Start the server manually on the remote: npx -y t3@0.0.24 serve --host <tailnet-ip> --base-dir ~/.t3
    2. Copy the printed pairing URL.
    3. In the desktop, Add environment → paste pairing URL (not the SSH launch flow).

    That path uses the non-schema-validated remote pairing code and connects without issue. The remote server still does the heavy lifting; only the bootstrap-bearer-session IPC step is bypassed.

    The fix suggested in the original report (Schema.DateTimeUtc → Schema.DateTimeUtcFromString for fields decoded from HTTP JSON) still looks like the right resolution — happy to test a patched build if a draft PR shows up.

  5. Pearcekieser commented on May 28, 2026

    @Pearcekieser

    I've experienced the same issue on 0.0.23 (holding off on upgrading until patched). I was able to correct it with a patch like #2694 I hope we can get this fixed soon.

  6. tomashomey commented on May 28, 2026

    @tomashomey

    Still reproduces on desktop 0.0.24 (Linux AppImage) connecting to a remote t3@0.0.24 server over SSH launch. Same root cause as reported for 0.0.23.

    The remote server starts fine and the loopback tunnel works:

    INFO (#191): Listening on http://127.0.0.1:3773
    

    …but bootstrap fails on the desktop:

    Error invoking remote method 'desktop:bootstrap-ssh-bearer-session':
    DesktopSshRemoteApiError: SSH remote API request failed during bootstrap-bearer-session.
    
    [cause]: SchemaError: Expected DateTime.Utc, got "2026-06-27T21:07:51.808Z"
        at ["expiresAt"]
    

    Confirmed in isolation with the bundled effect@4.0.0-beta.59 that Schema.DateTimeUtc rejects every
    JSON-serializable shape, so no server response can satisfy the current AuthBearerBootstrapResult decoder:

    import { Schema } from "effect";
    const dec = Schema.decodeUnknownSync(Schema.DateTimeUtc);
    dec("2026-06-27T21:07:51.808Z"); // throws: Expected DateTime.Utc, got "..."
    dec(1782680871808);              // throws
    dec(1782680871);                 // throws
    // Schema.DateTimeUtcFromString is the correct wire codec (as in #2694)

    PR #2694 (DateTimeUtc → DateTimeUtcFromString for expiresAt/issuedAt/createdAt/lastConnectedAt)
    fixes this. Requesting a merge + a stable release that includes it — remote SSH is currently unusable on
    the latest stable build without running a fork/patch. Thanks!

  7. techwithanirudh commented on May 31, 2026

    @techwithanirudh

    yeah, i have the same issue. i'm connecting to a remote server. i'm on desktop 0.0.24 (macOS Tahoe)

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