Repository navigation
Description
Assisted by Claude Opus 5.5 using T3 Code.
Summary
0.1.3 changed the server's default bind from 127.0.0.1 to 0.0.0.0. On a headless Linux host where tailscale serve already publishes the T3 port (3773), Tailscale holds <tailnet-ip>:3773. The 0.1.3 candidate's listen(0.0.0.0:3773) fails with EADDRINUSE, the candidate exits, and the service launcher rolls back to 0.1.2. Every retry fails the same way. The desktop client then shows "update failed" for that environment, and the message stays after the remote has been updated some other way.
Two issues:
- Main: the server update can't succeed on a Tailscale Serve host unless
T3CODE_HOST=127.0.0.1is set first. The error doesn't mention the bind-address change. - Minor: the Windows desktop app kept showing the remote update as failed after the remote was updated to 0.1.3 manually. Restarting the app cleared it.
Environment
| Remote server ("Mirror") | Client ("light-star-one") | |
|---|---|---|
| OS | Linux Mint 22.1 (Xia), Cinnamon, kernel 6.8.0-147-generic | Windows 11 Pro 10.0.26200 (build 26200) |
| T3 | Headless background service (t3code.service, systemd user unit, t3 __service-launcher). 0.1.2 → 0.1.3 |
T3 Code (Beta) desktop 0.1.3 (T3 Code (Beta).exe ProductVersion 0.1.3.0); t3 CLI v0.1.3 |
| Tailscale | 1.104.2 | 1.104.2 |
| Connection | Desktop connects to Mirror over the tailnet through Tailscale Serve HTTPS |
Mirror's Tailscale Serve config (hostname redacted):
https://<mirror>.<tailnet>.ts.net:3773 (tailnet only)
|-- / proxy http://127.0.0.1:3773
https://<mirror>.<tailnet>.ts.net:8443 (tailnet only)
|-- / proxy http://127.0.0.1:3773
Before the update, the systemd unit set no host (only T3CODE_HOME and T3_BOOT_SERVICE_UNIT), so 0.1.2 used its default.
Steps to reproduce
- Run a 0.1.2 headless service on Linux with the default host, and publish it with
tailscale serve --https=3773 http://127.0.0.1:3773. - Trigger the server update to 0.1.3 from the desktop client, or let the service self-update.
- The candidate fails to start and the launcher rolls back to 0.1.2. Retries fail the same way.
Evidence
Source: ~/.t3/userdata/logs/boot-service.log on Mirror (the systemd unit's stdout/stderr). Stack frames, pairing tokens and QR codes removed.
0.1.2 bound loopback by default. Every 0.1.2 start shows:
[22:59:36.166] INFO (#227): Listening on http://127.0.0.1:3773
The 0.1.3 candidate tries 0.0.0.0 and hits the port Tailscale holds. The 0.1.2 service then comes back on loopback:
[23:22:11.574] INFO (#303699): Server update prepared; handing off to the service launcher.
{ updateId: 'ade5dc4b-…', targetVersion: '0.1.3', runtimePath: '/home/<user>/.t3/runtime/versions/0.1.3/t3' }
[23:22:14.332] ERROR (#2): ServeError:
at Server.onError (file:///home/<user>/.t3/runtime/versions/0.1.3/t3:153776:19)
at server.runServerCommand (file:///home/<user>/.t3/runtime/versions/0.1.3/t3:421304:203) {
[cause]: Error: listen EADDRINUSE: address already in use 0.0.0.0:3773
}
[23:22:15.399] INFO (#6432): Listening on http://127.0.0.1:3773
The same handing off → EADDRINUSE 0.0.0.0:3773 pair appears for all 6 attempts: 23:22:11, 01:43:25, 01:43:44, 01:43:52, 01:45:27 and 09:06:01 (2026-10-28/29, local time EDT). During this period, ~/.t3/runtime/service-state.json showed status: rolled-back.
Who holds the port. ss -ltnp on Mirror after the fix:
LISTEN 100.x.x.x%tailscale0:3773
LISTEN [fd7a:…]%tailscale0:3773
LISTEN 127.0.0.1:3773 users:(("node-MainThread",pid=…))
The tailscale0 sockets belong to tailscaled. ss run without root doesn't show their owner, but they match the Serve config above.
A wildcard 0.0.0.0:3773 bind conflicts with the tailscaled socket on the tailnet IP. Binding 127.0.0.1:3773 doesn't.
Workaround (confirmed)
Pin the host with a systemd drop-in, then update from the CLI:
# ~/.config/systemd/user/t3code.service.d/host.conf
[Service]
Environment=T3CODE_HOST=127.0.0.1systemctl --user daemon-reload
t3 update -y
Result (same log):
[09:22:26.956] INFO (#6518): Listening on http://127.0.0.1:3773
[09:22:27.197] INFO (#6519): Removed unused t3 runtimes.
{ versions: [ '0.1.2' ] }
service-state.json now shows {"protocol":3,"activeVersion":"0.1.3"}.
Stale "update failed" in the desktop client (minor)
After Mirror was on 0.1.3 and healthy, the Windows desktop app (0.1.3) still showed the Mirror update as failed. Neither machine had that state on disk: there was no failure in Mirror's service-state.json or logs, and nothing in the desktop's %USERPROFILE%\.t3\userdata logs or SQLite stores. So it seems to be in-memory client state. Restarting the desktop app cleared it. Expected behavior: the client rechecks the remote's activeVersion on reconnect and clears the failure.
Suggested fixes
- Option A: keep
127.0.0.1as the default for the background service. At minimum, use it whentailscale servealready targets the port. - Option B: if
0.0.0.0stays the default, have the pre-update check (or the candidate's failure report) detectEADDRINUSEand name the cause and fix. For example: "port 3773 is in use, probably by Tailscale Serve; setT3CODE_HOST=127.0.0.1." Today the client only gets a generic rollback. - Call out the default-bind change in the 0.1.3 release notes, since it's a breaking change for Tailscale Serve setups.
Activity
ranvier2d2 commentedon Oct 30, 2026
Same root cause on the macOS desktop app, not only Linux service installs.
Version: T3 Code (Nightly) desktop
0.1.5-nightly.20261029.3877(darwin arm64), where the bug happened. Still the expected behavior on0.1.6-nightly.20261030.3917, since the desktop bind host comes from the exposure mode. The triage CLI wast30.1.5.Setup
serverExposureMode: "network-accessible"). The desktop then binds0.0.0.0(apps/desktop/src/backend/DesktopServerExposure.ts,DESKTOP_LAN_BIND_HOST).T3CODE_PORT=3773by a login agent.https://<host>.<tailnet>.ts.net:3773 → http://127.0.0.1:3775. Its target was dead, but Tailscale's network extension (root) still listens on<tailnet-ip>:3773.What happened
On login the backend crash-looped 9 times in 45 s:
The startup dialog says the server "could not start (code=1)" and gives no hint that a Tailscale Serve listener on the same port number is the cause. On macOS,
netstat -anvshows the owner asio.tailscale.ipn.lsofrun as the user shows nothing on 3773, which makes this hard to diagnose.Knock-on effect (may belong in #23372 / #22820): an auto-update relaunched the app without the env var, so the backend fell back to port 3775. It then spent 201 s in migration
73_ActiveTurnItemIndexes(twoCREATE INDEXonorchestration_v2_projection_turn_items, 170 s + 31 s, on a 23.5 GBstatev2.sqlite), with no window. The user quit after 55 s, and the backend kept running and held the owner lock. The next two launches showed "This T3 home is unavailable … Another T3 Code server, such as the background service, is using this data directory". No other server or service existed.Workaround that works: Connections → local-only, keep built-in Tailscale Serve on, and set
tailscaleServePort: 3773indesktop-settings.json. The backend binds127.0.0.1:3773, and T3 publisheshttps://…ts.net:3773 → http://127.0.0.1:3773itself. Both coexist with no conflict, and the tailnet URL and ACLs don't change.Suggestions
EADDRINUSEfor a wildcard bind, checktailscale serve statusfor the same port and say so in the error. At least point out that local-only mode avoids it.tailscaleServePort. Making it settable, or documenting it, would let people keep an existing:3773tailnet URL.Related PRs
0.0.0.0, the root of this issue.127.0.0.1afterEADDRINUSE, but only for the implicit managed-service bind. The desktop's network-accessible mode passes0.0.0.0explicitly, so as written it would not cover this case.73_ActiveTurnItemIndexes, the 201 s startup migration above.Diagnosed and written with Claude Opus 5.5 (Claude Code) via
t3 triage.LightCC commentedon Oct 31, 2026
@ranvier2d2 - Yeah, I ran across that "another server is using the data directory" somewhere along the way as well
juliusmarminge commentedon Nov 12, 2026
This is the same problem as #25161, so I'm closing it in favour of that one. If it is different, say how and it will be reopened.