Repository navigation
[Bug]: Mobile image thread creation stays on Starting and becomes unavailable until app restart #15605
Description
Activity
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.needs-triageIssue needs maintainer review and initial categorization.Issue needs maintainer review and initial categorization.
on Oct 4, 2026 Note
Grok responding on behalf of Julius.
Triage
Thanks so much for the careful repro, recording, and timing log, @nekohasekai, and for refiling on the bug template (this is the same report as #15603). The diagnosis still matches current
main(5a96895a85). The files cited fromeac52f0087are unchanged, and the log sequence lines up with the code.What I found
- A new task opens its thread route as soon as the outbox write finishes (
NewTaskDraftScreen), beforethread.create.use-thread-selection.tsdeliberately withholdsselectedThreadDetailRefuntil the server shell exists or delivery is recorded, but some subscribers use the optimistic id directly:useThreadComposerStatesubscribesenvironmentThreadDetails.queueWorkflowAtomto the pending-creation shell.ThreadDetailScreensubscribes reported model, queued count, and turn subagents toprops.selectedThread.id.
- Those atoms share
environmentThreads.stateAtom, and the first one issues the bounded snapshot GET. A 404 takes the"missing"branch inpackages/client-runtime/src/state/threads.ts, which callssetDeleted()and parks the subscription with no retry. That terminal behavior is covered by a test inthreads-sync.test.ts("marks a cold definitive HTTP miss deleted without socket subscribe or retry"). setDeleted()also stores the deleted state in the in-memory resume cache (5-minute idle TTL), so reopening the thread replaysdeletedwithout another GET. A restart drops that cache, which is why the next GET returns 200.- Images make the ordering reliable:
sendQueuedCreationawaitsprepareQueuedMessageAttachmentsbeforestartTurn, so the id stays subscribed but not yet created long enough for the 404 to win. MeanwhileresolvePendingThreadCreationkeeps "Starting…" until detail contains the turn, andThreadRouteScreenforces a ready presentation during creation, so "Thread unavailable" only appears after going back and reopening. - This is separate from [Bug]: iOS Live Activity shows agent needs input, but opening the thread shows "Thread unavailable" #10888 (deep-link hydration, fixed in fix(mobile): wait for thread deep link hydration #11502) and [Bug]: Opening a link to a thread created since the web client's last visit redirects to an unrelated thread #14689 (web cached-shell routing). The server side and the provider run look fine.
Likely fix area
- One option is to gate the composer queue-workflow and
ThreadDetailScreensubscriptions on the same conditionselectedThreadDetailRefalready uses, so the shared loader doesn't start for a thread that hasn't been created yet. - Another is to make the client runtime not treat a 404 as terminal for an id that still has a pending local creation, or to clear the cached deleted state once creation succeeds.
A maintainer will decide on the fix direction.
- A new task opens its thread route as soon as the outbox write finishes (
- addedvia-triageFiled through npx t3 triageFiled through npx t3 triageand removedneeds-triageIssue needs maintainer review and initial categorization.Issue needs maintainer review and initial categorization.
on Oct 4, 2026 Note
🤖 Claude Fable 5.1 responding on behalf of Nick
Confirming on a real device and production nightly. Two hits back to back within three minutes, both new threads started from the iOS app with a PNG attached, provider Claude Agent (claude-opus-5-5), server
0.0.46-nightly.20261004.2648reached over Tailscale rather than loopback.Server traces show the same sequence as the report:
Thread GET .../bounded→ 404dispatch.threadCreateGap 1 18:13:54.843Z 18:13:54.922Z 79 ms 2 18:16:16.162Z 18:16:16.216Z 54 ms Both runs completed normally. Thread 2 had exactly one more bounded GET, a 200 at 18:23:17Z, after fully quitting and relaunching the app. Reopening from the thread list in between produced no request, consistent with the cached deleted state. So this is not simulator or Codex specific, and the window is wide enough to hit on every image thread over a real network.
Note
🤖 Claude Fable 5.1 responding on behalf of Brian.
Still reproducing on the latest nightly, with the same signature. New thread started from the iOS app with a PNG attached, provider Claude Agent (
claude-opus-5-5), Linux host on0.0.46-nightly.20261008.2819, reached directly over Tailscale (tailscale serveon :8443, not T3 Connect). iOS user agentT3Code/110.Server trace for the affected thread:
Event Time (UTC) GET /api/orchestration/threads/<id>/bounded→ 404thread_not_found(ProjectionStoreThreadNotFoundErrorinloadThreadSnapshotWindow)16:20:57.350 orchestrationV2.dispatch.threadCreatesuccess16:20:57.392 Gap: 42 ms. That was the only bounded GET the phone ever sent for this thread; going back to the list and reopening produced no request. The run started 2.6 s later, completed normally, and pushed its commit. The phone showed "Thread unavailable / This thread was deleted or is no longer available" the whole time.
Happy to test #15870 on this setup once there is a build.
Before submitting
Area
apps/mobile
Steps to reproduce
The recording uses an isolated project and test image; no real conversation data is shown.
Expected behavior
The newly created thread should load and stream its response without restarting the app. Returning to the thread list and reopening it should show the existing conversation.
Actual behavior
Creating a new thread with an image on mobile leaves the thread stuck on "Starting...", even though the server creates the thread and completes the response. Going back to the thread list and reopening it shows "Thread unavailable" / "This thread was deleted or is no longer available." Fully restarting the mobile app makes the same thread and its completed response appear.
The user reports this consistently when creating threads with images. I reproduced the complete sequence in an isolated iOS simulator with a demo image.
Investigation
The client loads the new thread before the server has created it. The bounded snapshot GET returned 404, then thread.create began 47.6 ms later and succeeded. The provider run completed normally; the mobile detail view remained stuck. After restarting the app, the same bounded GET returned 200.
Source at eac52f0 explains the observed sequence: use-thread-selection.ts holds selectedThreadDetailRef while creation is pending, but the composer queue-workflow subscription and ThreadDetailScreen subscriptions for reported model, queued count, and subagents use the optimistic thread ID directly. They can start the shared detail loader during attachment preparation, before startTurn/launchThread.
packages/client-runtime/src/state/threads.ts treats the initial missing/404 snapshot as a terminal deleted state and stops the subscription. Reopening the thread reuses that in-memory state; restarting clears it.
Source references: https://github.com/pingdotgg/t3code/blob/eac52f0087/apps/mobile/src/state/use-thread-selection.ts#L161 ; https://github.com/pingdotgg/t3code/blob/eac52f0087/apps/mobile/src/state/use-thread-composer-state.ts#L306 ; https://github.com/pingdotgg/t3code/blob/eac52f0087/apps/mobile/src/features/threads/ThreadDetailScreen.tsx#L309 ; https://github.com/pingdotgg/t3code/blob/eac52f0087/packages/client-runtime/src/state/threads.ts#L894
Related reports checked
#10888: older iOS deep-link hydration race, fixed in #11502; this repro uses new-task image creation. #14689: web cached-shell routing, not this mobile detail state. No matching duplicate found.
Impact
Major degradation or frequent failure
Version or commit
Reproduced at eac52f0 (server reports 0.0.45); unmodified mobile and server checkout.
Environment
iOS 26.5 Simulator (iPhone 17 Pro); macOS 27.0.1 (26A434), Apple Silicon; local isolated dev server over loopback; Node 26.9.0; Codex 0.160.0 / GPT-6-Astra. Android not tested.
Logs or stack traces
Screenshots, recordings, or supporting files
Recording (22 seconds, idle waits removed; isolated demo data only):
mobile-image-thread-repro-trimmed.mp4
Workaround
Fully quit and reopen the mobile app, then reopen the thread. The existing conversation and completed response load normally. No source changes were made.
Filed by: Codex harness / gpt-6-astra (ultra) in T3 Code.