Repository navigation
Closed
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Description
What happened
I tried to snooze a thread that looked idle and got this toast:
Failed to snooze thread
Thread<id>has a queued run and cannot be snoozed.
Nothing in the thread looked queued or running. The last run had completed and the thread showed as idle.
Why this happens (from the local state DB)
The thread's runs in orchestration_v2_projection_runs looked like this:
| ordinal | status | requested_at (UTC) | note |
|---|---|---|---|
| 45 | cancelled | 02:17:25 | cancelled at 02:46:29, when the app restarted for an update |
| 46 | queued | 02:45:02 | PR-watch notification (userMessageId: message:pr-watch:…), payload has queueHeld: true, queuePosition: 1 |
| 47 | completed | 02:50:07 | my next manual message |
Here's my reading of the code on main (1e2ecbd975):
- A PR-watch update was queued (run 46) while run 45 was active.
- The app restarted.
ProviderRuntimeRecoveryServicecancelled run 45 and setqueueHeld: trueon run 46 ("require explicit consent before draining them"). - When I sent a new message, run 47 started right away and completed. Run 46 stayed held.
startNextQueuedRunreturns early whenever any queued run hasqueueHeld === true, so nothing drains it without an explicitqueue.resume. - The
thread.snoozeguard inOrchestrator.tsrejects onruns.some(run => run.status === "queued"). It doesn't check whether the queue is held, so the thread can't be snoozed until someone resumes or cancels the held run.
The same restart left a second thread in the same state: a held PR-watch run at ordinal 23, while run 24 was running.
Expected
Any of these would fix it:
- Snooze works on threads whose only queued runs are held. Held runs wait for consent anyway, so they don't need to block a snooze.
- If held runs should block snooze, the error says the queue is paused after a restart, and the UI offers to resume or discard the held messages.
- Sending a new message to a thread with a held queue either asks about the held items or makes them clearly visible, so a held run doesn't sit behind newer completed runs.
Question
Is it intended that the queue stays held after a new manual message has already started and finished a run? And should held queued runs block thread.snooze?
Environment
- T3 Code (Nightly) 0.0.46-nightly.20261004.2657, macOS (Apple Silicon)
- Provider: Codex
Reacted by Max Hoffmann
Metadata
Metadata
Assignees
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Activity
juliusmarminge commentedon Oct 5, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks @kr4chinin for digging into the local state and laying out the run table. That made it easy to trace. This reproduces from the code on
main(1e2ecbd975), and the toast matches thethread.snoozerejection:Thread <id> has a queued run and cannot be snoozed.What I found
On restart, queued runs that haven't started are marked
queueHeldso they aren't sent without consent. A new message is only queued behind a run that ispreparing,starting,running, orwaiting, and a held run is none of those, so your manual message (run 47) started and completed on its own while the PR-watch run stayed held. Draining it still takes an explicit resume (empty-composer Resume on web and desktop, Resume queue on mobile).The held run here is a PR-watch notification, not a user message. The queue UI hides notification and delegated-completion runs, and the thread shell skips held runs when deciding what looks current, which is why the thread appears idle. Settle already treats those automatic queued runs as non-blocking and cancels them, but the snooze guard (
runs.some(run => run.status === "queued")inOrchestrator.ts) has no such exception, so the hidden run blocks snooze.User-authored held messages are different: they stay visible in the queue and wait for resume.
Likely fix area
Workaround for now: settle the thread, which cancels the hidden PR-watch run, then unsettle it if you want it in the active list and snooze. Resume would deliver the held notification as a turn instead of dropping it.
A maintainer will decide on the fix direction.
topitopongsala commentedon Oct 10, 2026
Another reproduction, on
0.0.46-nightly.20261010.2908(macOS desktop, canonical~/.t3). Here it also causes chats that orchestrate children to stall for hours.After the app restarted at 2026-10-10 14:20Z:
queuedruns inorchestration_v2_projection_runscarriedqueueHeld: true. They sat in 7 threads across 4 projects, and the oldest was from 2026-10-08.cancelledrun and afailedrun, until they were cancelled by hand at 14:35Z.The only recovery through the MCP tools today is
t3_queue_read, thent3_queue_cancel, then sending the text again. There is no queue-resume tool. Two things would remove the stall:queue.resumein the UI and in MCP;smagnetize commentedon Oct 11, 2026
Another reproduction on
t3code-nightly-bin 0.0.46_nightly.20261010.2935(Arch Linux desktop, Claude Code via the Agent SDK). It adds two things to the 2026-10-10 comment:queueHeld: true: 69 PR-watch updates and 14 scheduled-task wakes, the oldest from 2026-10-07. If someone pressed Resume, the thread would wake about 88 times on stale PR states. Across the install, 288 held runs sit in 13 threads.A held queue would be much less risky if PR-watch dropped an update for a PR that is already merged, and replaced an older held update for the same PR instead of adding another.
naxey commentedon Oct 11, 2026
Note
Claude Opus 5.5 responding on behalf of @naxey.
Another reproduction of the held follow-up stall described above, on
0.0.46-nightly.20261009.2886(aa8c6662), affecting operator messages and MCPt3_thread_sendfollow-ups between orchestrating agents. It adds the mechanism and scale.What I saw. Messages queued behind a running turn stopped being delivered, and agents that orchestrate other threads stalled because their follow-ups never arrived. Messages to an idle thread still started normally, so it looked random. I remember queued follow-ups working when I installed T3 Code on 2026-10-06; I first noticed this stall after updating to the 2026-10-09 nightly. This is my observed timeline, not a verified version regression.
Mechanism. A restart hold spreads to every message queued afterwards.
apps/server/src/orchestration-v2/Orchestrator.ts:5011-5016onmain@ed0f7b90:The held-queue inheritance clause is also present in the installed nightly and was introduced in #2829; the
worktreeMissingRuncondition came later in #17654. A new queued message is created withqueueHeld: truewhile any held queued run remains, and it is not sent automatically when the turn ends. Resume releases the queue; cancelling all held entries also removes the block. Idle sends are not blocked by held runs (isBlockingRun), so the thread keeps working while queued follow-ups pile up behind the old hold.Nothing tells the sender:
t3_thread_sendreportsdelivery: "queued"but not that the run is held. MCP queue list/read results also omit the held flag, and the MCP queue tools have no resume operation.Evidence. These are every transition to
queueHeld: truefrom a previous value other than true, including runs created already held. Source:orchestration_eventswhereapplication_event_version = 2, history through 2026-10-11 14:21:47Z.No hold transition joins a
thread.stoporrun.interruptreceipt. The three transitions without a command ID can't be classified from receipts.One Claude thread: run 211 was held by the reconcile at 20:49:28. Afterwards, 16 new queued runs (259 … 334) were created held. State after run 330 completed at 2026-10-11 14:03:26Z:
Workaround used. Re-sent selected messages by hand; the original held entries stayed in the queue.
Environment: NixOS, Electron 44.3, Node 24.20; Claude Agent SDK (Opus 5.5) and Codex (GPT-6.1-Sol). Database read read-only; source checked against the installed source map and
main. Related PRs: #14617, #17885, #15201, #16926.WhiteWarrior625 commentedon Oct 28, 2026
Held ordinary queues still prevent snooze after #19099
A thread with held ordinary messages and no pending input cannot snooze. The snooze guard rejects those queued runs without checking
queueHeld, while dispatch refuses held queues. The thread can neither hide while waiting nor advance without changing held work.This follows #15862 and #19099's automatic-notification fix. Pinned main
b88da91397c247b98c892831d7e759f72ea4db43, the checked published Nightly, and installed bytes retain the held ordinary-message rejection. The observed root had 114 queued runs, all held, no pending runtime input, and a live machine job. Native self-snooze returned the queued-run error. Its work and queue must remain intact.Full Orchestrator integration also found a second consequence of permitting held queues to snooze:
queue.resumeandqueued-run.startrelease the hold and start work without clearing snooze. The proposed patch handles both transitions. Snooze ignores held runs, and a validated queue-release command clears snooze when queued work exists. Rejected releases leave the snooze and held work intact. Ordinary unheld messages still block snooze. Pending input and approval checks remain intact.Reproduction uses a real in-memory database and production services. Dispatch a first message, queue a second, interrupt the first with
holdQueue:true, then snooze. The unmodified source rejects snooze. Applying only the held filter passes preservation, new-message and pending-input checks, but both release commands fail the actual client's visibility calculation. The combined patch passes those release checks.Validation passed 85 tests across control reads, queue order, finalization, stop and steering, plus six focused runtime snooze tests. Real normalized pending user-input and command requests reach the persisted shell and actual client calculation. Existing automatic notifications stay snoozed and ordinary new messages wake. Lint and formatting pass. Package typecheck has the identical missing
typescript-legacydependency diagnostic on untouched baseline and proposed source in this filtered scratch install; a clean full typecheck is not claimed. No live runtime patch, queue change, process interruption, goal mutation or Stop-hook change was applied.Proposed production diff, with regression tests retained in the staged patch: