Repository navigation
Description
Before submitting
- I searched existing issues. Closest are [Bug]: Mobile thread list never shows Done after a turn completes #4952, [Bug]: Mobile looks idle while background subagents are still working #4962 and [Feature]: Use neutral status copy while reconnecting after mobile app resume #5695 — this is a distinct, more severe failure (see "Relation to existing issues"). Not [Bug]: Android store app disconnects immediately after server.getConfig — RPC protocol skew with all installable server builds #4901 (fixed) and not [Bug]: Mobile cannot connect to online relays after updating to 0.0.32-nightly.20260730.958 #5031 (relay-specific; this is pure Tailnet, no relay).
- I included server-side traces to reproduce/investigate.
Area
apps/mobile
Summary
Over a direct Tailscale tailnet connection from a distant cellular link (no T3 Connect relay, no LAN), the mobile thread list goes stale and never reconciles: rows for threads whose provider session has already stopped keep showing Working, and a newly created thread is missing from the list — even while the WebSocket is connected, RPCs succeed, and the user is actively inside the new thread. Only a full app restart (cold re-hydration) corrects the list.
The server state is correct and current the entire time; the desktop sidebar (same server) shows the right state. This is a mobile thread-list projection/reconciliation bug, made visible by an unstable (churning) remote socket.
Steps to reproduce
- Server: T3 Code desktop 0.0.32 (macOS arm64), embedded server, reached over a direct Tailscale tailnet IP (
http://100.x.x.x:3773). No relay, no Tailscale Serve. - Client: T3 Code Android app, current Play Store build (the one that resolved [Bug]: Android store app disconnects immediately after server.getConfig — RPC protocol skew with all installable server builds #4901). Phone on cellular, physically far from the server (high latency, NAT churn) — not on the same Wi-Fi.
- Have two threads whose provider sessions have since stopped (turns completed a while ago).
- Open the app and create a new thread, send one message, get a reply.
- Look at the thread list.
Expected behavior
- The two stopped threads show a quiescent state (timestamp / ready / Done), not Working.
- The newly created thread appears in the list.
- Matches the desktop sidebar for the same server.
Actual behavior
- Both stopped threads keep showing Working indefinitely.
- The new thread does not appear in the list, even though it exists, is in the same project, is not deleted/archived, and the user is actively inside it.
- The composer/stream inside the open thread also did not update live; the sent prompt and the reply only became visible after force-quitting and reopening the app. After that cold restart, the whole list is correct.
Server-side evidence
The backend is event-sourced (orchestration_events, monotonic sequence). Read straight from the server's state.sqlite and server.trace.ndjson at repro time (all UTC):
Authoritative server state — correct and current:
| Thread | Server truth | Mobile showed |
|---|---|---|
| Thread A | session stopped since 20:20:40 (event seq 143721) |
🔴 Working |
| Thread B | session stopped since 20:55:40 (event seq 143776) |
🔴 Working |
| New thread | thread.created at 22:00:32 (event seq 143832), same project, not deleted/archived, turn completed 22:00:37 |
🔴 absent from list |
provider_session_runtime at screenshot time: exactly 2 threads running (the active chat + the new thread's session); A and B are stopped. Latest server event: seq 143945 @ 22:05:04.
Mobile WebSocket sessions (UA okhttp), socket lifetimes:
| Session | Socket lifetime | RPCs dispatched |
|---|---|---|
| 21:58:04 → 21:59:13 | 68.5 s | getConfig, orchestration.subscribeThread, dispatchCommand, vcs.* |
| 22:00:26 → 22:02:46 | 140.1 s | getConfig, several orchestration.subscribeThread, server.probe, dispatchCommand (the new-thread send), vcs.* |
| 22:04:18 → 22:04:28 | 9.9 s | getConfig, 3× subscribeThread, vcs.* |
| 22:11:10 → 22:11:18 | 7.4 s | getConfig, server.probe, vcs.* |
| +5 more | never complete markConnected/markDisconnected | partial |
Two measured facts:
- The socket is not instant-dropping (that was [Bug]: Android store app disconnects immediately after server.getConfig — RPC protocol skew with all installable server builds #4901, now fixed): it lives 68 s and 140 s, does real RPC, and establishes per-thread subscriptions. But over the far cellular link it churns — long sessions interleaved with 7–10 s sockets and several reconnect attempts that never complete.
- During the healthy 140 s session the client issued many per-thread
orchestration.subscribeThreadcalls (andvcs.*), but no thread-list-level subscription or refetch (nosearchThreads, no list subscription). The new thread was created mid-session (seq 143832, 22:00:32) yet never appeared, and the two stopped rows never transitioned off Working.
Analysis (measured vs. inferred)
- Measured (server): server state is correct throughout; socket connects and does unary RPC; socket churns on the distant link; per-thread subscriptions are opened, but the thread-list surface receives no live channel during a connected session.
- Inferred (client, cannot measure from the server): the mobile thread list is hydrated once (cold start) and then relies on live deltas that either aren't delivered to the list surface or aren't reconciled after a reconnect gap. A full app restart forces fresh hydration and fixes it. A reconnect after a churn gap should re-fetch / replay from the last applied sequence so list-level events (
thread.created, session-status transitions,thread.settled) can't be silently missed.
Relation to existing issues
- [Bug]: Mobile thread list never shows Done after a turn completes #4952 ("never shows Done after a turn completes") assumes the client learns the session went quiescent but lacks a Done presentation path, falling back to timestamp. Here the row is stuck on Working and never transitions at all — the client never learns the session stopped. Complementary but a different (more severe) failure.
- [Bug]: Mobile looks idle while background subagents are still working #4962 is the inverse (idle while subagents still work). Not this.
- [Feature]: Use neutral status copy while reconnecting after mobile app resume #5695 (neutral copy while reconnecting) is related reconnect UX, but this is a data-staleness bug, not copy.
Version or commit
Server: T3 Code desktop 0.0.32 (also reproduced against a 0.0.31 self-hosted Linux server). Client: Android Play Store build as of 2026-08-08 (post-#4901). I don't have the exact mobile build hash.
Environment
Android native app, direct Tailscale tailnet IP over cellular, phone far from the server. No T3 Connect relay and no LAN Wi-Fi involved. The server is a macOS desktop 0.0.32 embedded server.
Workaround
Force-quit and reopen the app to force a cold re-hydration of the thread list.
Activity
ryanarmstrong commentedon Sep 6, 2026
Looks like I am running into the same issue. Force-quit resolves the issue for me as well.
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 in current main. The current mobile client uses a live V2 shell snapshot stream that carries thread-list changes, replacing the one-time list hydration path described in the report. Please test on the nightly built from main on 2026-10-21, or on the next stable release. One part is still open: reconciliation after a churning remote socket is not explicitly verified. If that part still matters to you, please open a new issue for that part only.
Techie5879 commentedon Oct 31, 2026
Reproduced on October 30, 2026.
Setup:
0.1.6-nightly.20261030.3940, confirmed from the live server identity and runtime metadata.A newly created thread was missing from the phone's list. Links to it rendered as plain "Thread no longer available", with no usable link.
Server checks confirmed the thread and project existed, messages were readable, and the agent was running. The thread was not deleted, archived, snoozed, or settled. The reference used the correct environment ID.
In the inspected source, mobile
useLiveThreadLinkLabelsreads titles from locally loaded thread shells;relabelThreadLinksreplaces an unresolved reference with that unavailable message. This is not evidence of server-side deletion.Recovery: pinned the thread through the server, returned to the phone's thread list, and reopened the conversation. Aritra confirmed access was restored. No server restart, app force-quit, or direct database edit was needed.
Likely stale client thread-list state. We did not establish socket churn as the trigger or isolate pinning from navigation as the recovery step.
Posted by GPT 6.1 Sol on behalf of Aritra.