Repository navigation
Description
Before submitting
- I searched existing issues and found related reports, but this is a distinct case.
- I included enough detail to reproduce or investigate the problem.
Area
apps/mobile
Steps to reproduce
- Have the iOS app connected to a Windows desktop environment, with a few active threads (one of them running a turn is enough).
- Open the app (cold launch, or come back to it from another app) and tap into an existing thread.
- Watch the thread view.
This happens on good Wi-Fi, not only on a weak connection.
Expected behavior
The thread's messages show right away. If the app has already seen this thread, the cached history shows while fresh data loads in the background, and newer messages come in as they arrive.
Actual behavior
The thread is completely blank for 5+ seconds. A "Loading messages..." pill shows first, then "Syncing messages...", and only after both do the messages show. While a turn is running, the only thing on screen is the "Thinking" row above the composer, with nothing above it (screenshot below). Until it fills in, you can't read the thread, scroll it, or tell whether anything is wrong, so the app can't be used for those seconds every time a thread is opened.
Impact
Major degradation or frequent failure
Version or commit
Desktop: T3 Code (Nightly) 0.1.3-nightly.20261023.3579. iOS app from the App Store (build not captured).
Environment
iPhone (iOS) connected to Windows 11 x64 desktop; provider Claude Code
Logs or stack traces
# Desktop server trace (server.trace.ndjson), the minute of the screenshot (19:55 local = 17:55 UTC).
# Note: the desktop app had restarted at 17:52:55 UTC (nightly update), and startup ran a migration
# (CREATE INDEX orchestration_v2_projection_turn_items_stop_request_idx: 25.5 s), so this sample is
# from just after a restart. statev2.sqlite is 20.9 GB on this machine.
17:55:06.604 http.orchestration.loadThreadSnapshotWindow 5159ms
orchestrationV2.threadManagement.ensureLegacyTranscript 3717ms
sqlite: SELECT thread_id, transcript_imported_at
FROM orchestration_v2_legacy_imports WHERE thread_id = ? LIMIT 1 3717ms
sql.transaction > ProjectionStore.readCanonicalProjection 1329ms
# Other single-row lookups on statev2.sqlite in the same few seconds also took 1-3.4 s
# (projection_nodes by node_id, scheduled_tasks, MAX(sequence) FROM orchestration_events),
# so the request was waiting on the database, not doing real work.
# For comparison, in the 18 minutes before the restart the same endpoint took ~10-15 ms
# (a few outliers at ~300-470 ms). So in steady state most of the wait on the phone
# is not explained by the server; the client's load/sync path seems to be the bigger part.
#
# One more steady-state pattern from the same traces: a local client (127.0.0.1, UA "node")
# fetched /api/orchestration/threads/:id/bounded for 30 threads at the same instant, and
# EnvironmentAuth.authenticateHttpRequest -> SessionStore.verify queued behind each other,
# so the later requests spent 1.1-3.4 s just authenticating (a one-row session lookup).Screenshots, recordings, or supporting files
Screenshot (iPhone, 19:55): thread "help rework th..." in pyseph-portfolio, a blank thread view with only the "Thinking" row and the "Loading messages..." pill above the composer.
Workaround
None; waiting it out.
Notes
Related, not duplicates:
- [Bug]: "Loading messages..." forever on iOS app #9414 / [Bug]: Mobile thread stuck on "Loading messages..." forever when the snapshot fails schema decoding #10206: "Loading messages..." forever (stuck, not slow)
- [Bug]: Desktop thread permanently stuck on "Syncing messages..." — hydration status never reaches
live; survives full relaunch; cleared only by removing IndexedDB #19563 / [Bug]: iOS 2.0.0 (103) repeatedly reconnects when opening one existing thread, including after un-settling #15365: stuck or looping on "Syncing messages..." - [Nit]: No "starting agent" state while the provider boots; only "Syncing messages..." shows for ~15s #14244: "Syncing messages..." shown while the provider boots
- [Bug]: iOS relay resume repeatedly waits on reconnection; Paseo comparison and recovery references #16418: reconnect wait when resuming the iOS app over the relay
Ideas that would fix most of it from the user's side:
- Show cached messages right away while the thread hydrates, instead of a blank view.
- Don't block the first paint on "Loading messages" followed by "Syncing messages"; render the bounded snapshot as soon as it arrives and sync after.
- On the server, keep a burst of requests from waiting behind each other on one SQLite connection for auth and single-row lookups.
Activity
fix(client-runtime): reopening a running thread shows its cached mess…
Pyseph commentedon Oct 24, 2026
Happened again just now. Neither the iOS app nor the desktop had been restarted, so the restart in the original report doesn't explain it. The thread list sat on syncing for over 6 s after the app came back to the foreground.
These are the server's spans for the phone's requests (desktop
server.trace.ndjson, times in UTC, phone =T3Code/164 CFNetwork/3860.700.2 Darwin/25.6.0/T3Code/2.1.0 (iPhone; iOS 26.6.2)over the T3 relay, iPhone Air):So for this sync:
payload_jsoninorchestration_v2_projection_threadstotals 2.7 MB), and as far as I can tell the phone downloads the whole shell again on every foreground/reconnect.statev2.sqliteis ~21 GB here).Suggestions, beyond the ones in the report:
I couldn't get the iOS app's own logs off the phone, so this is the server side only. I'm happy to attach client logs if the app has a way to export them.
Pyseph commentedon Oct 24, 2026
@shivamhwp heads up for #24210: I added a new trace above (#24192 (comment)) from a repeat with no restart on either side. The thread snapshot was fast this time (165 ms). The wait came from the phone's full shell sync on reconnect:
GET /api/orchestration/shelltook 10 s, 2.2 s building it and about 7.4 s sending it over the relay, for 2,157 threads. Showing the cached thread right away, as your PR does, should still cover the blank view as long as opening it doesn't wait on that shell sync.fix(client-runtime): reopening a running thread shows its cached mess…