Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/web
Steps to reproduce
- In one project, have three finished threads created in this order: "Document the price helpers", "Add currency formatter", "Add a clamp helper".
- Right-click "Add currency formatter" in the sidebar, choose Snooze, then In 3 hours. It moves to the Snoozed shelf.
- Open "Add a clamp helper".
- Click its title in the header, choose Delete, then Confirm.
Expected behavior
T3 Code opens "Document the price helpers", the next thread visible in the sidebar. "Add currency formatter" stays hidden until its wake time, as docs/user/thread-sidebar.md describes for snoozed threads. Settling or snoozing the open thread already skips parked threads and goes to the next visible one.
Actual behavior
T3 Code opens "Add currency formatter", the snoozed thread. The header shows it and the status line reads "Snoozed, 3h left · Wake now". A settled thread behaves the same: deleting the open thread opened a thread from the Settled shelf.
Cause: deleteThread passes every thread of the environment to getFallbackThreadIdAfterDelete, which keeps the threads of the same project, drops the deleted ones, sorts by recency and takes the first. It never checks whether a thread is snoozed or settled.
t3code/apps/web/src/hooks/useThreadActions.ts
Line 552 in 60aec28
| const fallbackThreadId = getFallbackThreadIdAfterDelete({ |
t3code/apps/web/src/components/Sidebar.logic.ts
Lines 1213 to 1238 in 60aec28
| export function getFallbackThreadIdAfterDelete< | |
| T extends Pick<Thread, "id" | "projectId" | "createdAt" | "updatedAt"> & ThreadSortInput, | |
| >(input: { | |
| threads: readonly T[]; | |
| deletedThreadId: T["id"]; | |
| sortOrder: SidebarThreadSortOrder; | |
| deletedThreadIds?: ReadonlySet<T["id"]>; | |
| }): T["id"] | null { | |
| const { deletedThreadId, deletedThreadIds, sortOrder, threads } = input; | |
| const deletedThread = threads.find((thread) => thread.id === deletedThreadId); | |
| if (!deletedThread) { | |
| return null; | |
| } | |
| return ( | |
| sortThreads( | |
| threads.filter( | |
| (thread) => | |
| thread.projectId === deletedThread.projectId && | |
| thread.id !== deletedThreadId && | |
| !deletedThreadIds?.has(thread.id), | |
| ), | |
| sortOrder, | |
| )[0]?.id ?? null | |
| ); | |
| } |
Settle and snooze navigate with planForwardNavigation, which walks the sidebar order and skips settled and snoozed threads:
t3code/apps/web/src/components/Sidebar.tsx
Line 1594 in 60aec28
| const planForwardNavigation = ( |
The fallback came in with #1395 ("Choose the next visible thread by sidebar sort order"), before settled and snoozed threads existed, and was not updated for them.
Impact
Minor bug or occasional failure
Version or commit
main @ 60aec28 (reproduced on a dev server built from 0847a44). v0.1.6 has the same code.
Environment
Linux, Chromium 151, Node 26.12.1, Replay (dev) provider threads
Logs or stack traces
No response
Screenshots, recordings, or supporting files
Session replay: https://bugreels.com/sessions/f623fa8b-2314-4033-87bb-15d4a769a688/recording?t=301 (sidebar before the delete at 5:01, delete dialog at 5:26, the snoozed thread opened at 5:35 and still open at 7:24)
Workaround
No response
Drafted with Claude Code (Claude Opus 5.5). I reproduced it on a local dev server and checked the cause against the linked source.
Activity
Iweisc commentedon Nov 3, 2026
@juliusmarminge Btw, was this bug report helpful? 😛