Repository navigation
[Bug]: Opening a link to a thread created since the web client's last visit redirects to an unrelated thread #14689
Description
Activity
Note
Grok responding on behalf of Julius.
Triage
Thanks for the careful repro and the timed captures, @saphid! This is a real web bug, and it's still present on current
main(a3abb52660).ThreadRouteView.tsxandthreadRoutes.tsstill match the lines you cited.To answer your question: a thread link should wait until that environment's shell is live before deciding the thread is missing. While it waits, any content that's already loaded should stay on screen.
What happens today
bootstrapCompleteisshell.data?.snapshot._tag === "Some", so any snapshot counts, including the cache saved from your last visit. The shell does report its own status (empty,cached,synchronizing, orliveinpackages/client-runtime/src/state/shell.ts), and on a warm start it stayssynchronizinguntil the new session gets a fresh snapshot. The route never checks that status.resolveThreadRouteRenderStatereports the thread asmissingonce that check passes and the thread isn't in the snapshot, unless thread detail or a local draft has already loaded. The route then replaces the URL with/.IndexDraftLandingstarts a draft on the most recently active project, which is the unrelated New thread in your capture.allEnvironmentShellsBootstrappedAtomalso treats any snapshot as enough, so that draft can come from the same stale cache. Once the URL has been replaced, the live snapshot that arrives later has no way to send you back. The thread only shows up in the sidebar.Intended behavior
- While the shell is
cachedorsynchronizing, stay on/<environmentId>/<threadId>and don't redirect to/. - If thread detail or a local draft for that link has already loaded, show it while waiting. Otherwise keep the existing loading state.
- Once the shell is live, the current redirect is correct: if the thread is still missing or marked
deleted, leave the route. - A sync error doesn't prove the thread is gone, so the app shouldn't open a new thread from the stale cache. Mobile already handles this in
thread-route-hydration.ts: it keeps waiting while the shell issynchronizingand only gives up onshellHasErrorordeleted.
Web already follows the same rule elsewhere.
createAllEnvironmentProjectSnapshotsReadyAtomignorescachedandsynchronizingsnapshots before it decides a saved project no longer exists.Desktop uses this same web route, so it should have the same bug. Mobile doesn't, thanks to its deep-link gate.
Related
- Not a duplicate of [Bug]: iOS Live Activity shows agent needs input, but opening the thread shows "Thread unavailable" #10888 (closed issue) or fix(mobile): wait for thread deep link hydration #11502 (merged PR). Those dealt with the iOS race and mobile's wait for hydration.
- fix(web): keep legacy thread routes stable during reconnect #10604 (closed PR) addressed this same web behavior. It was closed because the loading transition wasn't verified, not because the approach was rejected. A new fix should include before-and-after evidence: a stale cache, a thread created elsewhere, and the link still on that thread once the shell is live.
- While the shell is
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 2, 2026 - added a commit that references this issue
on Oct 3, 2026
Before submitting
Area
apps/web
Steps to reproduce
/<environmentId>/<newThreadId>.In my run, step 3 used the server's HTTP dispatch endpoint (
thread.create), and steps 1, 2 and 4 used headless Chromium. Everything ran against an isolated local server with synthetic data, and the server and browser talked directly, with no transport manipulation.Expected behavior
The link opens the new thread, which exists on the server, or at least waits until the client knows whether it exists.
Question for triage: what is the intended behavior while a client is still synchronizing? Should a thread link wait until the shell is live before it is treated as missing, and should already-available thread content be shown meanwhile?
Actual behavior
The client redirects to
/and lands on an unrelated thread (the environment's New thread). It stays there after synchronization finishes, even though the target thread appears in the sidebar by then.Measured in the run below: about 0.2 s after loading, the app is still on its splash screen at the target path. By 3.1 s the path has changed to an unrelated thread's, and it is unchanged at 8.1 s.
Likely cause, from source on
main:apps/web/src/components/ThreadRouteView.tsxtreats the route as bootstrapped as soon as any shell snapshot exists (bootstrapComplete = shell.data?.snapshot._tag === "Some", line 85 at094fb230), and that includes a cached snapshot that predates the thread. With no detail loaded yet, the thread resolves as missing, and the effect navigates to/.Impact
Minor bug or occasional failure
A link to a thread created since the client's last visit (from another device, a notification, or an agent) opens the wrong thread, and the user has to find the right one manually.
Version or commit
The web client was built from
pingdotgg/t3code@35be904.ThreadRouteView.tsxandthreadRoutes.tsare unchanged from there through currentmain094fb230. The server was the same source, run withnode apps/server/src/bin.ts --mode web.Environment
macOS (Darwin 25.5, arm64), Node 24.12.0, headless Chromium (chromium-headless-shell 153.0.8010.12) at 1280×800. Only the web client was checked; desktop and mobile were not. The "Codex update available" toast in the captures comes from the test server probing the locally installed Codex CLI, and is unrelated.
Screenshots, recordings, or supporting files
Base
35be904: the link to Created elsewhere A lands on workspace / New thread (real time; GIF sampled at 10 fps, no cuts or speed changes):Full MP4 · Screenshot 3 s after loading
For comparison: the same steps with the closed candidate fix from #10604 (1cd6689)
The link to Created elsewhere B stays on that thread. This only illustrates one possible intended behavior; it is not a request to merge that PR.
Screenshot 3 s after loading
Related, but not duplicates: #11502 (mobile now waits for thread deep-link hydration) and #10888 (iOS "Thread unavailable").