Skip to content

[Bug]: Queued messages that run after newer steers never appear in the thread #16987

Closed

Description

@seatedro

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

  1. Open a thread long enough that the client loads a bounded snapshot (hasMoreHistory: true).
  2. While a run is busy, queue one or more follow-ups. Each gets its run ordinal when it is queued.
  3. Before the queue drains, send messages that start right away (stop, steer, or a new turn after an interrupt). These get higher ordinals and render normally.
  4. Let the queued runs drain. They start and complete, and the agent replies.

Expected behavior

The queued user messages and their replies appear in the open thread as they run.

Actual behavior

They never appear. The thread looks like it stopped at the last message sent directly, so the queued input looks swallowed. Runs that start afterwards render normally. Nothing is lost on the server: the queued runs complete, and their user_message and assistant_message items are in orchestration_v2_projection_turn_items.

In the affected thread, each queued run had a requestedAt 15 to 25 minutes before its startedAt. The directly sent runs between them had higher ordinals but started first.

Impact

Major degradation or frequent failure

Version or commit

0.0.46-nightly.20261007.2774 (server and desktop)

Environment

Self-hosted t3 serve on Linux arm64 with the Claude Code provider. Seen in the desktop app on a separate Linux desktop and on a Mac.

Logs or stack traces

No errors. Provider events show `command_lifecycle` queued → started → completed and `result` / `success` for each affected run.

Screenshots, recordings, or supporting files

No response

Workaround

Reloading does not help in clients that had the thread open when the queued runs drained. They restore the thread from their local cache and resume with afterSequence, so the missing rows stay missing.

A fresh snapshot from the server (GET /api/orchestration/threads/:id/bounded) does include them. Because rows are ordered by ordinal, they appear above directly sent turns that actually ran earlier.

Deleting the thread's entry from the thread store in the t3code:connection-runtime IndexedDB database, then reloading, brings the turns back.

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 7, 2026
  2. juliusmarminge commented on Oct 7, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for the detailed report. I can confirm this in code on main (3143335), though I haven't reproduced it in a running app. The queued run's items are dropped on the client by the partial-timeline "older history" guard, because their ordinal comes from the run's queue-time ordinal.

    Mechanism

    1. The ordinal is fixed when the message is queued. A queued follow-up gets nextRunOrdinal(projection) (runs.length + 1) at queue time (Orchestrator.ts#L4850-L4851, #L347-L349). Its user_message turn item is only created when the run starts, but with ordinal: queuedRun.ordinal * 100 (#L1584-L1623). The server then normalizes every run-scoped turn-item.updated into that run's band, runOrdinal * 1_000_000 + n (EventSink.ts#L309-L330, TurnItemPositionStore.ts#L58-L71). A steer or new turn sent later gets a higher run ordinal, so all of its items sort above the still-queued run's.
    2. The client keeps a high-water mark on bounded windows. When hasMoreHistory is true or a history cursor is open, the client applies events with partialTimeline: true and latestLocalTurnOrdinal, and raises that mark on every applied turn-item.updated (threads.ts#L501-L526). The mark starts as the max ordinal across the full projection, taken from the snapshot (threadHistoryPaging.ts#L384-L394).
    3. The guard drops anything at or below the mark. shouldDropMissingPartialTurnItem drops any item that isn't already in the window and has ordinal <= latestLocalTurnOrdinal, on the assumption that it's unloaded history (orchestrationV2Projection.ts#L89-L106). The turn-item.updated reducer then returns the projection unchanged (#L251-L258). Once a directly sent run's items have raised the mark, every item of a lower-ordinal queued run that starts later, its user message and its reply included, falls under it and is silently dropped.
    4. Reload doesn't recover them. The resume cursor still advances past the dropped events (threads.ts#L446-L468). The persisted snapshot stores that sequence (#L556-L566), and on reload the cached entry seeds lastSequence (#L221-L225), which is sent as afterSequence (#L920). The cache is the thread store in t3code:connection-runtime (storage.ts#L41-L45), so the dropped items are never replayed.
    5. A fresh snapshot shows them out of order. The server reads turn items ORDER BY ordinal ASC (ProjectionStore.ts#L2666-L2675), so the queued runs land above directly sent turns that actually ran earlier. That follows from the queue-time ordinal and matches what you saw.

    Fix direction (not yet decided)

    • Client: don't treat an item as unloaded history only because its ordinal is at or below the mark when its run wasn't running at snapshot time. For example, keep a missing item whose runId belongs to a run that started after the snapshot, or that was still queued in the client's projection, and insert it by ordinal as usual. That should stop the rows from vanishing. A test with a queued run N draining after run N+1's items would cover it.
    • Server ordering: the out-of-order placement after a fresh load is a separate question, whether a queued run's items should sort by when they started rather than when they were queued. That would mean allocating the run's position or band at start. It's a bigger change and a design call.
    • Existing caches: entries already persisted with the dropped items need a one-time refresh, for example by invalidating cached bounded thread snapshots or bumping the cache version, or affected threads will stay incomplete after the fix.

    Workaround: what you found works. Delete the thread's entry from the thread store in the t3code:connection-runtime IndexedDB, then reload, and the client fetches a fresh bounded snapshot.

    Related

  3. added
    via-triageFiled through npx t3 triage
    and removed
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 7, 2026
  4. Mkassabov commented on Oct 8, 2026

    @Mkassabov

    Also seen with the Grok provider on 0.0.46-nightly.20261008.2813 (desktop app on Linux x64 plus the Android app). Both clients showed the same thing, so it isn't one stale client cache.

    In one thread, follow-ups queued while a run was held open (Grok background subagents, see #15489) ended up like this:

    • Queued scheduled-task posts (runs 21 and 22, requested 09:30 and 09:35) were applied as steers into the older run 17 at 09:32:05 and 09:50:04, so their messages appear under run 17's later attempts.
    • Runs 23 and 24 (requested 09:40 and 09:45) went cancelled at 09:50:05–06 without ever starting a provider turn. Their user messages still exist at their original timestamps.
    • Run 25 (requested 09:50:34) started at 09:51:58, after the held run finally released.

    The user saw history "rewrite": steered messages showed up out of order, and some earlier user and agent messages vanished from the timeline. The vanished agent messages are mostly the superseded-attempt folds from #15489.

    Posted from a t3 triage session by Claude Code (Opus 5.5).

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

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions