Repository navigation
[Bug]: Async questions make a running thread show as Awaiting Input #15258
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 (also apps/mobile and packages/client-runtime)
Steps to reproduce
- Have a Codex thread post an async question (an
agentMessagewithdelivery: "async"andquestions). CodexAdapterV2 stores it as auser_inputruntime request withresponseCapability: { type: "message" }, and the question is designed to stay pending while the agent keeps working. - Don't answer it. Send the thread a normal message instead, or just let the agent continue.
- While the run executes commands, look at the thread in the sidebar or the mobile thread list.
I hit this with a delegated Codex child. It asked an async question, got the answer through a normal message 13 seconds later, acknowledged it and kept testing for over an hour. During that time task_status reported running/working and pendingRequestCount was 1. Not answering through the request API is why the question stayed open, and that part is expected.
Expected behavior
A thread whose run is still executing should read as Working, possibly with a separate marker for the open question. "Awaiting Input" or "Input" should mean the run is actually blocked waiting for an answer.
Actual behavior
The row says Awaiting Input (legacy sidebar and status pill) or Input (sidebar v2 and mobile), with no Working indicator or elapsed time. It is also left off the Working shelf. A genuinely blocked question shows exactly the same status, so an agent that's still working looks stalled.
Cause:
ProjectionStorebuilds the shell'spendingRuntimeRequestfrom the newest pending request but keeps onlyid,kindandcreatedAt.responseCapabilityis dropped (OrchestrationV2PendingRuntimeRequestSummary).presentThreadShell(packages/client-runtime/src/state/models.ts) setshasPendingUserInputwheneverkind === "user_input".resolveSidebarThreadStatusandresolveThreadStatusPill(apps/web/src/components/Sidebar.logic.ts) andresolveThreadListV2Status(apps/mobile/src/features/threads/threadListV2.ts) checkhasPendingUserInputbefore the runtime status.
The thread detail view already makes the distinction. derivePendingThreadRequests marks message-mode questions dismissible, and ChatView keeps its working state from the runtime. Only the list status loses it.
A focused test on 77823bd using presentThreadShell with status: "running" plus a pending user_input request gives:
runtime.status = running, hasPendingUserInput = true
sidebar v2 status = input | legacy pill = Awaiting Input | Working shelf = false
mobile list status = input
blocking question (runtime waiting): sidebar v2 status = input | pill = Awaiting Input
detail view, async: responseCapability=message, dismissible=true
detail view, blocking: responseCapability=live, dismissible=false
Possible narrow fix: add a flag such as blocking (or the response capability type) to the pending request summary. Only let blocking user_input requests override a running or starting runtime. Async questions would then still show while the run is active, for example as Working with a question badge, and take over the row once the run stops.
Impact
Minor bug or occasional failure
Version or commit
77823bd (main)
Environment
Linux desktop app, Codex provider
Workaround
Answer or dismiss the question through the question UI or t3_pending_request_respond. A normal message to the thread does not clear it.
Activity
juliusmarminge commentedon Oct 3, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks @btsouth for the precise root-cause trace and the focused test output.
Confirmed on main at
77823bd1. It's expected that the async question stays pending. What's wrong is the status the thread list shows.Codex stores an
agentMessagewithdelivery: "async"as a pendinguser_inputrequest withresponseCapability: { type: "message" }, and that request doesn't block the run (CodexAdapterV2.buildUserInputRequestArtifacts). The turn keeps running, and the thread detail view already marks the question as dismissible.The shell summary only keeps
id,kind, andcreatedAt(OrchestrationV2PendingRuntimeRequestSummaryinthreadShellFromProjection), sopresentThreadShellsetshasPendingUserInputfor everyuser_input. Several places check that flag before the runtime status:resolveSidebarThreadStatusandresolveThreadStatusPill: sidebar v2 shows Input, the legacy pill shows Awaiting Input, the row drops off the Working shelf, and the elapsed time disappears.resolveThreadListV2Status: mobile shows Input.ThreadNotificationCoordinator: the same transition triggers an "Input needed" toast.resolveThreadAwarenessPhaseV2: Live Activity and the relay reportwaiting_for_input.canSnooze: the running thread can't be snoozed.Likely fix area
Your narrow fix lines up with the code. Carry whether the request blocks (or its response capability) in the pending request summary, and only let blocking
user_inputoverride a running or starting runtime. Once the run stops, an open async question would still read as Input. A separate question marker while Working is optional.The existing tests for
resolveSidebarThreadStatus("prioritizes awaiting input over a running runtime") andisSidebarThreadWorkingassume blanket priority today, so they would need to split blocking questions from async ones.A maintainer will decide on the fix direction. Your workaround is right for now: answer or dismiss the question through the question UI or
t3_pending_request_respond.moshemiz06 commentedon Oct 12, 2026
Reproduced on 0.0.46-nightly.20261011.2994, Linux/Ubuntu host, Codex provider, using the installed client bundle and read-only inspection of the affected thread's durable state.
The user reports that the task stays out of the Working area and still says Input, even though there has been no recent question. The screenshot shows the purple question icon and Input badge.
What I found
user_inputruntime request was created at 2026-10-11 23:15:46.573 UTC withresponseCapability: { type: "message" }.pending, withresolvedAt: null. The event history has its pending update but no resolved/cancelled update for that request.t3_thread_readreportsstatus: "completed",activeRunId: null, andpendingRequestCount: 1.So the badge comes from an older open async question. The latest run is currently completed, so it should not be in Working at this instant. The bug is that this old nonblocking question also excludes the task from Working whenever a later run starts. A lack of recent questions alone does not establish that the old request was answered; I did not find a resolution event and am not claiming that an answer was lost.
Reproduction from the installed nightly
I extracted the actual status and Working membership functions from
client/assets/useHandleNewThread-qaHmTPtV.js(utandve) and executed them in a Node VM. WithhasPendingApprovals: false,hasPendingUserInput: true, default interaction mode, and a later run:The Working function exits on
hasPendingUserInputbefore evaluating runtime activity. The installedruntime-DsY2nu5w.jsalso derives that flag solely frompendingRuntimeRequest?.kind === "user_input", losing the nonblocking/message distinction described above.Minimal user flow:
Expected: while the later run is executing, show Working and place it in Working, while keeping the optional question separately accessible. Preserve normal Input handling for genuinely blocking questions.
The Working shelf is intentionally status-driven and not a drag/drop destination (
Sidebar.logic.tsexplicitly rejects it), so dragging the card cannot correct this classification. That explains why it feels impossible to move the task down.Validation limit: I verified the existing affected state and reproduced the classification directly from the installed nightly functions. I did not create a fresh UI thread or perform a UI drag:
preview_openfailed withPreview automation open failed: This host blocks the sandbox T3's browser runs in (AppArmor on Ubuntu 23.10+).No live thread state was changed.MaximilianMauroner commentedon Oct 13, 2026
Note
gpt-6.1-sol · Codex responding on behalf of the user
Additional case: answering a repeated async question leaves the original pending
Verified in an active T3 Code thread. This adds a request-lifecycle case to the async-question status classification discussed here:
t3_thread_readreportedpendingRequestCount=1;t3_pending_request_listreturned exactly the original request, andt3_pending_request_readconfirmed it asked the same decision.Before cleanup, the authoritative listing reported
activity=awaiting_input,hasPendingUserInput=true,hasPendingApprovals=false,isRunning=true, andstatusReason="open because user input is pending". The agent had accepted the user's choice, but the server still held the original question open.The primary agent applied the user's already-given answer to that specific duplicate through
t3_pending_request_respond. The pending list then returned[], withactivity=running,hasPendingUserInput=false,hasPendingApprovals=false, andstatusReason="open because session is running". No new product choice, permission approval, or production setting change was involved.Proposed workflow fix, not implemented: When explicitly resurfacing the same pending question, reuse its request identity or supersede only that duplicate. Preserve unrelated pending questions and approvals. This request-lifecycle change is separate from the status-display policy discussed in this issue.
Regression acceptance: Ask an async question, end the turn, repeat it, answer the replacement, and verify no orphan duplicate or “Input needed” status remains. Also verify that two distinct unanswered questions remain pending.
Direct mobile visual reproduction was not performed, so a mobile cache or rendering defect is unverified. Affected server/mobile versions were not recorded in the supplied evidence. No app source fix or fresh reproduction was performed for this comment.
juliusmarminge commentedon Oct 19, 2026
Note
Grok responding on behalf of Julius.
Fixed by #15430, which just landed on main. Pending request summaries now carry a
blockingflag, so an async (nonblocking) question keeps a running thread showing Working in the sidebar, status pill, and mobile list, and only switches to Input once the run ends. Blocking questions and secret requests still show Input as before. Thanks @btsouth for the detailed trace.The repeated-question lifecycle case mentioned above (answering a re-asked async question leaving the original one pending) isn't changed by this PR. If you still hit that or a stuck Input badge on a current nightly, please open a new issue with the details.