Skip to content

Desktop app keeps offering a server update after the server already updated, and retrying fails with "Remote updates must select a newer server version" #32896

Closed

Description

@SeoFood

What happened

A Windows desktop app (Nightly) updated a remote macOS server (background service) to the newest nightly. The update succeeded and the server restarted on the new version. The desktop app kept showing the server update. Clicking it again failed twice with:

Server update failed: Remote updates must select a newer server version.

Reloading the window (F5) made the update offer go away.

Diagnosis

  • The server log shows the handoff to 0.1.8-nightly.20261108.4353 at 10:47:09 and a restart on that version at 10:47:12. The npm nightly tag points to the same version, so no newer server build exists.
  • Both later server.updateServerWithProgress calls (10:47:22 and 10:49:23, same client trace) end in ServiceLauncherRejectedError. The launcher's #handleUpdateRequest rejects when compareExactServiceVersions(targetVersion, child.version) <= 0. Each call took about 300 ms because the target runtime was already on disk, which means the target was the version already running.
  • On the client, collectServerUpdateTargets (apps/web/src/components/sidebar/updateEverything.logic.ts) picks servers by resolveVersionMismatch(config.environment.serverVersion, targetVersion). After the restart and reconnect, the client still holds the old serverVersion (0.1.8-nightly.20261107.4340) for that environment, so it still lists the server as a target for 4353. The cached config is only refreshed by a full reload.
  • The client also has no special handling for this rejection. It shows a generic error even though the server is already on the requested version.
  • main (8e6a732) has no changes to these files since v0.1.8-nightly.20261107.4340.

Steps to reproduce

  1. Run a remote server as the background service (t3 service install) on nightly N-1.
  2. Connect from the desktop app (Nightly) while nightly N is available.
  3. Start the server update from the desktop app's update capsule / "Update all". Wait for it to succeed and reconnect.
  4. The server update is still offered. Click it again: the error is "Remote updates must select a newer server version."
  5. Reload the window: the offer is gone.

Version

  • Desktop app (client, where the stale state lives): T3CodeNightly 0.1.8-nightly.20261107.4340 on Windows
  • Server: updated from 0.1.8-nightly.20261107.4340 to 0.1.8-nightly.20261108.4353 (background service, macOS)
  • Service launcher process: 0.1.1-nightly.20261016.3203
  • Triage CLI: 0.1.1-nightly.20261016.3203 (only the CLI that ran t3 triage)

Environment

  • Server: macOS 27.2 (darwin arm64), Node v26.8.2, launchd service, behind a LAN reverse proxy
  • Client: T3 Code Nightly desktop app on Windows 10/11 x64 (Electron 44.10.1)

Evidence

Server trace (`server.trace.ndjson`), `cloud.server_self_update.update` spans:

10:43:08–10:44:13  4x Failure  Could not prepare t3@0.1.8-nightly.20261108.4353 (PinnedRuntimeInstallError while downloading the t3 release checksums; cause not logged, likely unrelated)
10:46:43           Success     (26 s, download + handoff)
10:47:22           Failure     ServiceLauncherRejectedError: Remote updates must select a newer server version.  (296 ms)
10:49:23           Failure     ServiceLauncherRejectedError: Remote updates must select a newer server version.  (306 ms)

Service log:

[10:47:09.719] INFO: Server update prepared; handing off to the service launcher.
  targetVersion: '0.1.8-nightly.20261108.4353'
[10:47:12.823] INFO: Listening on http://0.0.0.0:3773

`/.well-known/t3/environment` afterwards: `serverVersion: 0.1.8-nightly.20261108.4353`, `serverSelfUpdate: boot-service`.

Related issues

Fix applied or workaround

Reload the desktop app window (Ctrl+R / F5) after a server update.

Filed by

Triaged with Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage.

Activity

  1. juliusmarminge commented on Nov 8, 2026

    @juliusmarminge
    Member

    Tried to reproduce this on current main (8e6a732) and could not trigger it yet.

    Setup: a real service launcher running the published t3@0.1.8-nightly.20261107.4340 Linux runtime (serverSelfUpdate: boot-service), paired as a remote environment to a client whose app version was 0.1.8-nightly.20261108.4353.

    • Per-server Update (Settings › Connections): handoff to 4353, restart, reconnect. The toast read "Reconnected on t3@0.1.8-nightly.20261108.4353", and the server update was no longer offered.
    • Settings › Connections › Update all, after resetting the service to 4340: same result. The update committed and only the provider updates stayed offered.
    • Plain restart of a remote onto a newer version: the client picked up the new serverVersion without a reload.

    Before the update succeeded, I also hit your first failure mode: GitHub returned 504 on the 4353 SHA256SUMS/archive, giving "Could not prepare t3@…".

    The path I could not drive is the desktop app's sidebar update capsule while a desktop update is pending. That is where localUpdateVersion becomes the target. To narrow it down:

    1. Which control did you click the second and third time: the sidebar update capsule ("Update all", with the desktop update downloaded), the per-server Update button in Settings › Connections, or Settings › Connections › Update all?
    2. Before you pressed F5, what version did Settings › Connections show for that server: 4340 or 4353?
    3. Was the desktop app's own 4353 update downloaded at that point? Did the run offer to restart the app, and did you postpone it?
    4. If it happens again, the desktop app's renderer console (View › Toggle Developer Tools) around the second click would help.
  2. SeoFood commented on Nov 8, 2026

    @SeoFood
    Author

    Thanks for digging in. Answers, plus a correction to my original diagnosis:

    1. Control: the Retry button on the sidebar update capsule, every time after the first failure.
    2. Settings › Connections version before F5: I didn't check, sorry.
    3. Desktop update: yes. The desktop app's own 4353 update was downloaded, and I postponed the restart. That fits the code: the capsule only targets a 4353 server while localUpdateVersion is 4353, because the app itself was 4340.
    4. Renderer console: I didn't capture it. I'll grab it if this happens again.

    Correction: the stale serverVersion explanation in the issue is probably wrong. The server trace shows the client reconnecting at 10:47:13, after the restart onto 4353 at 10:47:12. On reconnect it ran ws.loadServerConfig and subscribeServerConfig against the new server, before the first rejected call at 10:47:22. So the client should have had the new config. Also, the trace ID I called "the same client trace" is the server process's trace, not the client's.

    Server-side timeline (cloud.server_self_update.update): 4× "Could not prepare" (10:43:08–10:44:13). Success at 10:46:43, then handoff and restart. Then three rejections, not two: 10:47:22, 10:49:23, 10:51:33. Each one was "Remote updates must select a newer server version" and took ~300 ms. After that came the reload (reconnect at 10:52:03).

    A likely path in the code (apps/web/src/components/sidebar/updateEverythingRun.tsx at v0.1.8-nightly.20261107.4340, unchanged on main):

    • retryFailedUpdateRunServers re-sends the ServerUpdateTarget stored in the run (machine.server). It does not compare the target against the server's current serverVersion.
    • Unlike the main run path, the retry path records any outcome other than "updated" as a failure ("Could not start the update"). This includes "skipped", which is what an interrupted command returns. When that happens, machine.server stays set, so the capsule keeps offering Retry. The main path treats "skipped" as not failed.
    • With the desktop restart postponed, run.localVersion !== null, so closeFinishedUpdateRun never clears the run. The capsule stays until the in-memory run atom is lost, which is why F5 fixed it.

    I couldn't tell from the server side why the client didn't record the 10:46:43 retry as "updated", even though the server says it succeeded. Two possible fixes: have Retry drop targets the server already satisfies, and have the client treat "must select a newer server version" as "already up to date" when serverVersion >= targetVersion.

    Server environment, if useful: service launcher process is 0.1.1-nightly.20261016.3203 (it supports the prepared/committed trial protocol), child 0.1.8-nightly.20261108.4353, macOS 27.2 arm64.

    Triaged with Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage.

  3. letuwang commented on Nov 11, 2026

    @letuwang

    Note

    🤖 Claude Opus 5.5 (via t3 triage) responding on behalf of letuwang

    Another occurrence through the sidebar capsule's Retry button. This time the first failure was different, and the server was updated by hand rather than by the app.

    • Client: T3 Code Desktop 0.1.10-nightly.20261111.4529 (macOS). Server: Linux x64, background service, now on 0.1.10-nightly.20261111.4529 (= current nightly).
    • Server trace (cloud.server_self_update.update): at 11:54 and 12:00 (local), updates from 0.1.9-nightly.20261111.4501 failed with "installed service launcher speaks protocol 3, but t3@0.1.10-nightly.20261111.4529 requires protocol 4". The user then installed 4529 by hand. At 12:08:25, 12:08:41 and 13:42:48, Retry requested 4529 again. Each request was rejected in ~500 ms with ServiceLauncherRejectedError: Remote updates must select a newer server version.
    • Each rejected call ran cloud.pinned_runtime.ensure_installed against a runtime already on disk, so the target was the version already running.
    • Confirmed by the user: they clicked Retry each time, and they updated by hand between the failed and rejected attempts.

    This supports the retryFailedUpdateRunServers theory (apps/web/src/components/sidebar/updateEverythingRun.tsx, unchanged on main). It re-sends the stored machine.server target without comparing it to the server's current serverVersion. A successful in-app update isn't needed to trigger this: any failed run followed by an out-of-band update leaves a Retry that can never succeed.

    Suggested fix, unchanged: drop retry targets whose server already satisfies targetVersion, and treat this rejection as "already up to date" when serverVersion >= targetVersion. Workaround: reload the window.

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

    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