Skip to content

Shutdown-held queues never drain after CLI sends on idle threads #25780

Closed

Description

@arun92phoenix

T3 0.1.3-nightly.20261024.3604, macOS arm64, Codex provider.

After a server shutdown, queued messages can remain held indefinitely while subsequent CLI sends appear accepted and newer runs complete.

Minimal reproduction:

  1. Start a long-running Codex thread.
  2. Queue a second message using CLI thread send.
  3. Restart the T3 server while the first run is active.
  4. Inspect orchestration.getThreadProjection. Shutdown reconciliation sets queueHeld=true on the queued run.
  5. Send another message through CLI thread send, then allow the newer run to finish.
  6. Original queued run remains status=queued, startedAt=null, queueHeld=true; subsequent queued messages inherit the hold. No actionable hold explanation is returned by the CLI.
  7. Trying queued-message.promote-to-steer against a completed target fails; it cannot start an idle thread.

Observed evidence (anonymised): a queued run requested 2026-10-24T14:48:29.380Z received queueHeld=true at 16:01:58.594Z with command ID command:runtime-reconcile:shutdown::. Seventeen held runs remained on 27 October despite newer executed runs.

Bundled source explains this: startNextQueuedRun returns if any queued run has queueHeld=true. dispatchMessage releases held runs only for createdBy=user and creationSource=web/mobile; CLI/agent sends inherit the existing hold. The observed initiating hold was shutdown reconciliation, not an active-goal hold.

Expected: distinguish explicit user Stop from shutdown recovery. CLI auto-send should report held/no-start clearly and expose a documented supported resume path, rather than imply consumption. queue.resume is a workaround, but callers must know it is needed. A completed run cannot be a steering target; promotion UI/API should offer starting queued work when idle.

Activity

  1. omid3098 commented on Oct 27, 2026

    @omid3098

    Claude Opus 5.5 wrote this for the reporter of the case below.

    Another case of held queued runs that never drain, on 0.1.3-nightly.20261026.3717 (Windows). It involves no CLI send: all of the held runs are automatic delegate_task completion deliveries to a Claude parent thread.

    What the parent's rows in orchestration_v2_projection_runs showed:

    • Run 24, a delegated-task failure notice queued at 2026-10-27T08:53:52Z, stayed queued with queueHeld: true.
    • Four later completion notices came in while the parent was mid-turn, and each one inherited the hold: run 43 (10:49:49Z), run 44 (10:49:54Z), run 46 (11:01:18Z) and run 51 (11:12:57Z). Each was a Delegated task … [completed] delivery for a child that finished during one of the parent's turns.
    • Notices that arrived while the parent was idle started at once (runs 45, 47–50, 52–70). So the thread looked healthy, but it never received those four results. For about four hours its own reports listed the four finished children as still running.
    • t3_queue_list on the parent showed the five held runs. To recover, I cancelled them and passed the results to the parent with t3_thread_send.

    I could not find what set the first hold, because the server trace logs from that morning had already rotated. Around 08:45–08:53Z several delegated children, and a turn in another thread, failed with "Provider turn failed". So the hold may have come from the recovery after that failure, not from a shutdown.

    In this case the cost of the hold was silent: nothing on the parent or in the agent's tools said that its queue was held. A completion delivery that cannot start, or that inherits a hold, should either start once the parent is idle or be visible to the parent and the user as held.

  2. EtanHey commented on Oct 28, 2026

    @EtanHey
    Contributor

    Additional read-only evidence collected through the built-in T3 triage playbook, Codex / GPT-6.1 Sol, 2026-10-28. Current desktop and live server: official 0.1.4-nightly.20261028.3814, macOS arm64. The historical initiating build was not independently recorded, so this is not a claim that the initial hold began on .3814.

    An affected thread had 17 never-started held runs, requested between 00:18:16.737Z and 06:21:49.268Z. Its event history gives a concrete first cause: startup reconciliation at 00:45:51.723Z set queueHeld: true on runs 19, 21 and 22 (command:runtime-reconcile:startup:<redacted-thread>:2026-10-28T00:45:51.723Z). Later queued runs inherited the hold. Twelve of the 17 inputs were server-origin notices; five were MCP-origin messages. Newer runs executed while the old ones never started. The old runs were manually cancelled at 09:11:09.764Z–09:11:29.676Z; the oldest waited 31,973.027 seconds before cancellation, without starting.

    The same installed source releases held runs in dispatchMessage only for createdBy === "user" and creationSource === "web" || "mobile" (excluding scheduled tasks). Agent/MCP sends and automatic notices do not satisfy that condition. This explains why a healthy-looking thread and newer successful turns do not establish that its earlier queue drained. The latest published .3824 retains this release predicate; that newer app was not run here.

    A fresh t3_queue(action:"list") at 13:12:44Z also found six old PR-watch notices in another failed thread; its read-only run projections record queueHeld: true. The recovered first thread's queue is now empty.

    Separately, our agents sometimes explicitly sent mode:"queue" while intending to steer. That is our misuse and is not evidence of a T3 steer failure. We did not promote/cancel other work or write the database during this investigation. This case strengthens the recovery/held-queue report rather than alleging that every queued follow-up is broken.

  3. t3dotgg commented on Oct 31, 2026

    @t3dotgg
    Member

    Note

    🤖 Claude Opus 5.5 responding on behalf of Theo

    Closing: this is covered by #27221, which landed. Reopen if an important part is still missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions