Problem
On iPad, tapping a thread sometimes leaves the detail pane empty with only the "Syncing messages..." pill above the composer. The UI is then effectively locked up until the app is force-quit. Reopening the same thread after a restart often reproduces it. Started around 2026-10-02 and has been frequent since. Seen on iPad only so far; web and desktop not checked.
What I know
- The pill is the thread sync label (
apps/mobile/src/features/threads/ThreadDetailScreen.tsx ~358-370). It shows when the thread state is empty, cached, or synchronizing while content presentation is ready. So the screen believes it has content, yet the message list renders blank.
- The server does not look slow for this. In the server trace, the
sql.execute spans under ws.rpc.orchestration.subscribeThread peak at 34 ms. Caveat: the trace rotates about every minute at 10 MB, so it only covers roughly the last 12 minutes and nothing from yesterday or from a failing open.
- The affected thread (id
714471f7-67b1-4d97-80ae-53b9fdf511d6) is not extreme: 153 messages (about 100 KB of text) and 826 activities (about 2.2 MB of payload JSON). Other threads in the list are larger and I have not heard that they fail.
- "Same thread fails again after restart" points at state that survives a restart: the persisted thread cache (
cache.loadThread in packages/client-runtime/src/state/threads.ts ~207) or the server's snapshot for that thread, rather than a one-off connection hiccup.
- Force-quit being the only exit suggests the JS thread is blocked or the thread state never leaves
synchronizing. The spinner still animates, which fits a native animation on a blocked JS thread, but that is a guess.
Not yet known
- Whether the JS thread is hung (decode or render of a large snapshot or cache entry) or the stream is just never completing.
- Whether clearing the thread's cache on the iPad fixes it.
- Which iPad build is installed, and whether the start date matches a build change.
Next steps
- On the iPad, reproduce with a thread that fails, then check whether another thread still opens without force-quitting. That separates a blocked JS thread from a stuck stream.
- Try the same thread from web or desktop on the same server. If it loads there, the problem is iPad-side cache or decode.
- If cache is implicated, add a size or time bound to cache restore and a timeout that falls back to a fresh fetch.
- Add a timeout on the sync pill so a sync that never completes shows an error with a retry, not an indefinite spinner.
This is the kind of report the papercut capture (docs/fork/papercuts.md) is meant to make cheap: build SHA, sync phase, and a client event ring buffer would answer the first two unknowns directly.
Related: #2 (client stuck states with no timeout).
Problem
On iPad, tapping a thread sometimes leaves the detail pane empty with only the "Syncing messages..." pill above the composer. The UI is then effectively locked up until the app is force-quit. Reopening the same thread after a restart often reproduces it. Started around 2026-10-02 and has been frequent since. Seen on iPad only so far; web and desktop not checked.
What I know
apps/mobile/src/features/threads/ThreadDetailScreen.tsx~358-370). It shows when the thread state isempty,cached, orsynchronizingwhile content presentation isready. So the screen believes it has content, yet the message list renders blank.sql.executespans underws.rpc.orchestration.subscribeThreadpeak at 34 ms. Caveat: the trace rotates about every minute at 10 MB, so it only covers roughly the last 12 minutes and nothing from yesterday or from a failing open.714471f7-67b1-4d97-80ae-53b9fdf511d6) is not extreme: 153 messages (about 100 KB of text) and 826 activities (about 2.2 MB of payload JSON). Other threads in the list are larger and I have not heard that they fail.cache.loadThreadinpackages/client-runtime/src/state/threads.ts~207) or the server's snapshot for that thread, rather than a one-off connection hiccup.synchronizing. The spinner still animates, which fits a native animation on a blocked JS thread, but that is a guess.Not yet known
Next steps
This is the kind of report the papercut capture (docs/fork/papercuts.md) is meant to make cheap: build SHA, sync phase, and a client event ring buffer would answer the first two unknowns directly.
Related: #2 (client stuck states with no timeout).