Repository navigation
[Bug]: iPad (iOS app and Safari/Chrome web) reconnect-loops on a remote environment although the server sends the first config snapshot in ~250 ms #16549
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
iOS app / web client on iPadOS (shared client runtime, I think)
Steps to reproduce
- Run the T3 Code background service (0.0.45) on a Linux desktop, bound to
127.0.0.1:3773, exposed inside a tailnet withtailscale serve(HTTPS 443 →http://127.0.0.1:3773, tailnet only, no Funnel). - Pair an iPad (iPadOS 26.6) with the iOS app via
t3 pair --tailscale. - Pair a MacBook (macOS desktop app) the same way, on the same Wi-Fi network.
- Leave both connected.
Expected behavior
The iPad stays connected, as the Mac does.
Actual behavior
iOS app (build 106): the environment cycles between connected and reconnecting. From the server trace, each attempt goes:
/.well-known/t3/environment200 →POST /api/auth/websocket-ticket200 →authenticateWebSocketUpgradesucceeds →/wsupgrade.ws.rpc.subscribeServerConfig(andorchestration.subscribeShell) start.- About 14.9 s later,
subscribeServerConfigendsInterruptedwithClientAbort. - The
/wsrequest closes about 45 s after it opened, and the client retries.
This repeated for over an hour (dozens of attempts). A few connections lasted longer, the longest about 5 min, then the loop resumed.
The ~15 s abort looks like CONNECTION_ESTABLISHMENT_TIMEOUT from #14516. Unlike #14516, the server is not slow. In the same traces, the snapshot's discovery work finishes in well under a second:
externalLauncher.resolveAvailableEditors: max 287 msresolveUsableFileManagerCommand: ~110 ms- no other span on the connect path above ~1 s
So the server appears to produce the snapshot in roughly 250 ms, but the iPad client still hits its 15 s budget.
Network ruled out: the loop happens both through the Tailscale DERP relay (70–500 ms) and over a direct path (~30 ms, tailscale ping). Over the same tailscale serve endpoint, on the same Wi-Fi, the macOS desktop app (and Chrome on macOS earlier) stays connected without drops.
Web client on the same iPad:
- Chrome (iOS, CriOS 154):
/pairloads and all assets 200.POST /api/auth/browser-session200 consumes the pairing token, and the session is created. But the page stays on the T3 loading screen and never requests a websocket ticket or opens/ws.t3 auth session listshows that session withlast connected: never. Reloading/pairthen re-posts the consumed token, which gets a 401 (UnknownBootstrapCredentialError). - Safari (iPadOS 26.6, desktop-class UA):
/pair, the manifest and the icon load. No/api/auth/sessionor/api/auth/browser-sessionrequest follows, so the pairing token is never redeemed. It stays on the loading screen.
Impact
Major degradation or frequent failure. The iPad can't be used as a client.
Version or commit
- Server: t3 0.0.45 (background service, Linux x64)
- iOS app: T3Code/106 (CFNetwork/3860.700.1, Darwin/25.6.0)
- Working comparison: macOS desktop app (Electron), same server
Environment
- Server: Ubuntu 24.04.3 LTS, kernel 6.8, Node via nvm (service PATH resolves
claudefine) - Client: iPad Air, iPadOS 26.6, Low Power Mode off
- Transport:
tailscale serveHTTPS proxy, Tailscale 1.102.5 on the server
Logs or stack traces
Server trace (server.trace.ndjson), one representative iOS app attempt:
19:13:16 http.server GET /ws 44885ms Success (closed)
19:13:16 ws.rpc.subscribeServerConfig 14868ms Interrupted: InterruptError ... at ClientAbort
├ externalLauncher.resolveAvailableEditors 227ms
└ externalLauncher.resolveUsableFileManagerCommand 108ms
19:13:48 (next attempt, same shape)
Chrome on iPad:
19:22:43 POST /api/auth/browser-session 200 (PairingGrantStore.consume Success)
19:22:43 GET /api/auth/session 200
19:22:47 GET /pair 200 (repeats; no /api/auth/websocket-ticket, no /ws)
19:23:32 POST /api/auth/browser-session 401 UnknownBootstrapCredentialError
I don't have client-side traces from the iPad. Happy to collect them if you can point me at how.
Workaround
None on the iPad. The macOS desktop app works.
Activity
juliusmarminge commentedon Oct 25, 2026
Additional report and server traces: #24909, iOS 2.1.0 (164), server nightly 20261023.3579. Reconnects continue after fixing the host tailscale CLI access: configuration loads in 0.4–1.4 s, but sockets still last 101 s, 71 s and 0 s, with overlapping old/new sockets. Thread snapshots take 0.1–0.4 s; opened threads can remain on Syncing.