Repository navigation
Description
Before submitting
- I searched existing issues. Related reports are [Bug]: iOS app fails to open a thread on slow links ("syncing → failed to connect") after an image Read — server inlines full base64 images into thread activity payloads #4595, [Bug]: One slow foreground health probe tears down a live session and disables the composer #7231, and [Bug]: Remote web UI disconnects repeatedly on high-latency mobile connections, spamming disconnect/reconnect toasts and leaving threads desynced #2750, but I did not find an exact duplicate of this V2 thread-specific reproduction.
- I included a controlled comparison and sanitized server observations.
Area
apps/mobile
Steps to reproduce
Observed repeatedly on this installation; not yet reproduced with a synthetic thread on a clean installation:
- Connect the iOS TestFlight app to the Mac Nightly environment through T3 Connect.
- Open a small existing native thread and leave the app in the foreground. It stays connected during the observed control intervals.
- Open another existing native thread from Settled. Its messages appear, but the app repeatedly reconnects.
- Switch back to the small control thread. The connection recovers.
- Reopen the affected thread. The reconnect error returns.
- Explicitly un-settle the affected thread on the Mac without sending a prompt. Confirm it appears in the active list on both clients. Open it from the active list on the iPhone.
- It still shows persistent "Syncing messages..." and the list shows "Reconnecting to MacBook Air ...".
The settled reproduction was observed twice. The active-state test ran for approximately two minutes. The phone stayed in the foreground with Low Power Mode off, and the Mac remained awake. Only Nightly was running; Alpha was closed.
Expected behavior
Opening an existing idle thread should synchronize its history and keep the environment connected. Changing its sidebar settlement status should not be needed to access it.
Actual behavior
The affected thread enters a reconnect loop after its messages load. Observed messages include "Failed to connect. Retrying MacBook Air ...", "Syncing messages...", and "Reconnecting to MacBook Air ...". Earlier connection detail screens also reported a failed connection health check or a disconnected host with a generic DNS/firewall hint.
Other tested threads stay connected on the same phone, Mac, and network. Un-settling the affected thread does not resolve the failure.
Impact
Major degradation or frequent failure. The affected conversation is unreliable to use from the phone.
Version or commit
Mac: 0.0.46-nightly.20261003.2632.
iPhone: TestFlight 2.0.0, build 103. Server user agent identifies the client as T3Code/103.
Environment
MacBook Air, macOS arm64; iPhone over Wi-Fi during the controlled tests; managed T3 Connect Cloudflare tunnel. Mac awake, iPhone Low Power Mode off. The affected native T3 thread currently selects claudeAgent / Claude Opus 5.5. This is a client connection/synchronization failure, not a demonstrated provider failure.
No software update, re-pairing, history deletion, or security-policy change was performed during these tests.
Logs or stack traces
Sanitized server observations, October 4, 2026, Moscow time (UTC+03:00):
First affected-thread test:
01:06:35 SessionStore.markConnected
01:06:48 SessionStore.markDisconnected
01:06:53 SessionStore.markConnected
01:08:01 SessionStore.markDisconnected
01:08:08 SessionStore.markConnected
01:08:40 SessionStore.markDisconnected
01:09:01 SessionStore.markConnected
01:09:36 SessionStore.markDisconnected
Control:
01:10:15 SessionStore.markConnected
Small thread remains usable; no visible reconnect loop.
Reopen affected thread:
01:11:58 SessionStore.markConnected
01:12:12 SessionStore.markDisconnected
01:12:16 SessionStore.markConnected
01:12:23 SessionStore.markDisconnected
01:12:30 SessionStore.markDisconnected
After explicitly un-settling:
01:17:23 Projection confirms settledOverride=active, settledAt=null.
01:18:09 Affected-thread subscription starts; duration 31.27 s.
01:18:44 Replacement subscription starts; duration 31.59 s.
01:19:18 Replacement subscription starts; duration 27.88 s.
01:18 to 01:20 iPhone shows Syncing messages; list shows Reconnecting.
Concurrent environment descriptor, WebSocket-ticket, and shell requests return HTTP 200; WebSocket upgrades reach and are accepted by the server. Thread snapshot preparation in the observed cycles takes roughly 29 to 156 ms. No HTTP 401 was observed during this controlled reproduction. The server and tunnel processes remain alive with low CPU usage at a sampled point.
Session events can overlap during replacement, and subscription duration does not establish who initiated a close or its cause. The server trace does not expose enough client-side failure detail to determine the exact mechanism.
Read-only stored-data comparison:
| Affected thread | Small control thread | |
|---|---|---|
| Message records | 104 | 20 |
| Turn-item records | 338 | 20 |
| Turn-item JSON characters | 909,108 | 22,439 |
| Message JSON characters | 73,581 | 17,832 |
| Linked PRs | 3 | 0 |
The affected thread has three provider turns. These are SQLite storage measurements, not WebSocket frame sizes. Content size, rendering cost, and linked-PR work are investigation leads, not established causes. A scan found no data:image/ or base64, prefixes in its stored turn items, so the inline-image mechanism in #4595 is not established here.
Workaround
Leaving the affected thread and switching to a smaller tested thread allowed the connection to recover. The affected conversation can be viewed on the Mac. Un-settling it did not help.
Related observations and limits
A 20-second Home Screen suspension recovered in roughly four seconds during a separate test, and a cold app launch recovered. The failure above also occurs without backgrounding or locking the phone.
#4595 concerns inline-image activity payloads on a slow link; #7231 concerns foreground health-probe timeouts; #2750 concerns repeated mobile web reconnects. This report provides a newer native iOS V2 reproduction that persists on Wi-Fi and after un-settling. The exact cause remains unresolved. Mobile connection traces, WebSocket close reasons, and frame sizes during the failing subscription would help.
No conversation contents, credentials, signed asset URLs, or raw trace files are included.
Activity
sergeivad commentedon Oct 3, 2026
Follow-up controlled test on the same builds, October 4, 2026 (Moscow time):
Sending a new message from the Mac does not clear the failure.
This does not prove the internal cause, but neither un-settling nor appending a fresh successful turn from the Mac resolved the observed client failure. No update, re-pairing, history deletion, or security change was applied.
juliusmarminge commentedon Oct 3, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks for the careful, controlled comparison, @sergeivad! This looks like a real connection failure, and it isn't a duplicate of #4595, #7231, or #2750. Settlement isn't the cause. Un-settling only changes the sidebar override, and the thread subscription uses the same environment socket either way, which fits the loop continuing from the active list.
What the code shows
SessionStore.markConnectedandmarkDisconnectedbracket the whole socket inapps/server/src/ws.ts, so each subscription ending means the iPhone dropped the connection, not that the server closed one thread stream. Nothing on that path times out at 30 seconds: RPC pings go every 5 seconds, and the health probe runs only on wakeup with a 15-second deadline (packages/client-runtime/src/connection/supervisor.ts). Because the small thread stays up on the same tunnel, an idle-tunnel timeout is unlikely.The part that does match "only this thread" is the snapshot size. The 75-row / 1 MiB cold-open budget in
apps/server/src/orchestration-v2/threadHistoryPaging.tsapplies only when the timeline has no turn-start anchors. When anchors exist, the snapshot query inProjectionStore.tshas no row cap and keeps the latest user turns (up to 10) intact, with every tool item. So a three-turn thread is sent in full, andpayloadBudgetExceededis reported without truncating it. The wire snapshot also duplicates local rows intovisibleTurnItemsandturnItems, so it can be larger than the ~909 KB stored. "Syncing messages..." is the pillThreadDetailScreen.tsxshows when messages are on screen but thesynchronizedmarker hasn't arrived yet, which fits a socket that keeps dying before then.Likely fix area
visibleTurnItemsandturnItems.One check that would help
If you can, open this same thread in the desktop app or a browser through the same T3 Connect hostname. If that client also reconnects, the full snapshot is the likely cause. If only the phone does, the WebSocket close code from the iOS connection screen would be the next useful detail. No trace contents are needed.
A maintainer will decide on the fix direction.
sergeivad commentedon Oct 4, 2026
I tried the requested comparison using an in-app desktop browser at the same existing managed T3 Connect hostname (not localhost). I could not reach the thread: the web client stays on its boot splash screen, and the browser console reports:
The hostname is under *.t3coderelay.com. No certificate, authentication, or security protection was bypassed. This means the browser test is inconclusive for the snapshot hypothesis. Is there a supported client entry point for testing the same managed tunnel in a browser?
A separate mobile observation on the same builds: Settings > Environments temporarily showed 0 environments and "No additional linked cloud environments", while Account > T3 Connect still listed the linked Mac. Force-closing and relaunching only the iOS app restored the existing Mac to Environments with "Connected"; no re-pairing was needed. This restores the inventory, but does not establish that the affected large-thread disconnect is fixed.
The expanded iOS connection card currently shows Connected and managed-tunnel controls, without a numeric WebSocket close code. The failure screenshots previously attached show the human-readable health-check/disconnected messages. Where can I obtain the numeric close code in build 103?
jonnotk commentedon Oct 16, 2026
A server-side trace of the same symptom, from a web client on a self-hosted Linux server (0.1.1-nightly.20261016.3190, Docker, QNAP NAS). Opening one existing thread made the client reconnect, and the trace shows why: the thread snapshot query held the main thread for 29 seconds.
Setup.
statev2.sqliteis 3.19 GB, with 69,977 turn items and 333,032 events, on a two-disk 5400 rpm RAID1. The NAS had rebooted four hours earlier. The thread has 8,730 turn items (59 MB ofpayload_json, largest row 1.08 MB) and 326 runs.What happened, 2026-10-16 UTC (from
server.trace.ndjsonandevent-loop-stalls.ndjson):ws.rpc.orchestration.subscribeThreadws.orchestrationV2.threadSnapshotThenLive→sql.transactionsqliteWITH eligible AS NOT MATERIALIZED (...)(readCanonicalProjectioninProjectionStore.ts)SessionStore.verify(WebSocket upgrade auth), queued behind itScheduledTaskService.listDueTasks,UsageLimitRecoveryWorker.sweep,PRAGMA auto_vacuumEvent-loop stall records in that window:
delayMaxMs3,140 and 6,206,utilization0.9 and 1.0. The web client's RPC failed withSocketReadError: An error occurred during Read, and requests sent during the stall reached the server about 20 seconds late. The session-state read failed after its 6 s client timeout.Same thread, data in memory. A full scan of the thread's 8,730
payload_jsonrows took 0.1 s from Python against the same file, a few minutes later. So the 29 s came from reading pages from disk while the query ran on the main thread. 1,851 git commands from PR lookups were hitting the same disks at that time. That part is inferred: I did not drop the host's page cache to reproduce it.What would help. Any one of these keeps a slow disk read from freezing the whole server:
eligiblescan before it touchespayload_json. It filters every turn item of the thread and callsjson_extract(item.payload_json, ...)foruser_messagerows. A column forinputIntentandcreatedBy, or an index that covers the turn-start rows, would let the window pick its rows from the index alone and read payloads only for the selected rows.Related: #14701 (the thread list read blocking the loop, closed by #15033) and #20254 (VCS refresh stalls on a VPS).
t3dotgg commentedon Oct 21, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of Theo
Thanks for the report. One part of this is fixed on main in #15385. Please test on the nightly built from main on 2026-10-21, or on the next stable release. One part is still open: Anchored snapshots remain uncapped, and the reported 900 KB thread's reconnect loop is not proven fixed. If that part still matters to you, please open a new issue for that part only.