TUI return can leave stale busy spinner and active-turn inhibitor after subagent completion on Linux/tmux
What version of Codex CLI is running?
v0.118.0
What subscription do you have?
Pro
Which model were you using?
gpt-5.4 high
What platform is your computer?
Linux x86_64
What terminal emulator and version are you using (if applicable)?
tmux
What issue are you seeing?
I hit a bad TUI/runtime state around subagent switching.
The important operator action is that I was switching between the subagent and the main agent via /agents.
After the child subagent completed, the parent/main thread ended up in a stale busy-looking state instead of cleanly recovering.
From the user side this looked like the app hung or crashed while leaving the screen in a state that still suggested work was ongoing.
What I observed locally on the affected tmux pane:
- the pane content still contained repeated
Waited for background terminallines from the earlier work - the final assistant response from the parent thread was already printed
- the normal prompt was visible again at the bottom
- but the tmux pane title remained stuck at
⠧ home(busy spinner title) and did not clear - the title stayed exactly
⠧ homeacross repeated checks over multiple seconds, instead of returning to a non-busy title - the Codex process remained alive
- a stale child process was still present:
systemd-inhibit --what=idle --mode=block --who codex --why "Codex is running an active turn" -- sleep 2147483647- that inhibitor was still alive more than an hour after the turn had already completed
So even if the backend believes the turn finished, the TUI/runtime busy-state cleanup did not happen correctly.
This is not just “the turn completed and I did not want it to” (although from the operator side it did feel wrong). The concrete bug is that the CLI was left in a contradictory state:
- visible prompt is back
- parent turn has completed
- but busy indicators and the active-turn idle inhibitor are still stuck as if a turn were running
What steps can reproduce the bug?
I do not yet have a minimal deterministic repro, but the sequence that triggered it was:
- Start Codex CLI in
tmux. - Use a workflow that spawns/uses a subagent.
- Switch between the child and main thread via
/agents. - Switch back to the main thread and wait for the child subagent to complete.
- Observe that after completion, the main thread can be left in a stale state:
- the screen still looks like it was working recently (
Waited for background terminalstill visible) - the tmux pane title remains stuck on a busy spinner
- an
active turnsystemd-inhibitchild remains alive - the process is alive, but the session no longer feels cleanly interactive from the user perspective
Affected top-level thread ID:
019d61b9-5b70-78e2-9a11-56ebcfd12bba
Child subagent completion was delivered into that parent thread via <subagent_notification>.
What is the expected behavior?
After subagent completion and return to the main thread, Codex should do one of these consistently:
- keep the parent turn active and visibly keep waiting, or
- complete the parent turn and fully clear all busy state
It should not leave the session in a contradictory half-finished state where:
- the prompt is back,
- the backend turn is complete,
- but the busy spinner/title and the
Codex is running an active turninhibitor are still stuck.
Additional information
Concrete local evidence from this incident:
- parent thread rollout recorded
task_completeat2026-04-06T10:55:05.491Z - affected tmux pane title remained:
⠧ home- repeated checks over ~6 seconds showed the title was still unchanged:
t0: ⠧ homet+3s: ⠧ homet+6s: ⠧ home- the stale inhibitor process remained present long after completion:
systemd-inhibit --what=idle --mode=block --who codex --why Codex is running an active turn -- sleep 2147483647
Related but not identical issues:
#11472(sub-agent shutdown / parent restoration)#12033/#14303/#14314(staleWaiting/Waited for background terminalstate)#12645(tmux TUI/input stuck after completion)
My current guess is that this is another turn-lifecycle cleanup bug in the TUI/runtime layer, possibly triggered specifically by the /agents thread-switch path around subagent completion boundaries.
12 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Duplicate comment from the wrong account. Reposted above from
iqdoctor.Duplicate comment from the wrong account. Reposted above from
iqdoctor.Duplicate comment from the wrong account. Reposted above from
iqdoctor.Duplicate comment from the wrong account. Reposted above from
iqdoctor.Duplicate comment from the wrong account. Reposted above from
iqdoctor.Carrying over the concrete findings from my follow-up analysis, since this issue currently has the user report but not the code-level hypothesis.
I dug through the upstream TUI thread-switch path and there is a plausible logic hole around
/agentsrestores:codex-rs/tui/src/app.rsstore_active_thread_receiver()savesstore.input_state = self.chat_widget.capture_thread_input_state()when leaving the active thread.ThreadEventStore::push_notification()clearsactive_turn_idonTurnCompleted, but it does not sanitize the already-savedinput_statefor that inactive thread.codex-rs/tui/src/chatwidget.rscapture_thread_input_state()persists both:task_running: self.bottom_pane.is_task_running()agent_turn_running: self.agent_turn_runningrestore_thread_input_state()later restoresself.agent_turn_running = input_state.agent_turn_runningand immediately does:self.turn_sleep_inhibitor.set_turn_running(self.agent_turn_running)self.update_task_running_state()That creates this failure mode:
/agents.agent_turn_running=true/task_running=true.TurnCompletedarrives.active_turn_id, but the saved widgetinput_statestill says the thread is running.systemd-inhibitchild from stale snapshot state.That lines up with the exact symptom cluster here:
systemd-inhibit --why "Codex is running an active turn"remains aliveI also noticed the restore path appears to replay snapshot state before draining queued live events, which widens the race window further.
One more version note for attribution: the runtime artifact involved in the bug really was
@openai/codex 0.118.0(/usr/bin/codex->/usr/lib/node_modules/@openai/codex/bin/codex.js, andcodex --versionreports0.118.0).So my current best guess is that the fix is in one of these directions:
agent_turn_running/task_runningsnapshot flags as authoritative across/agentsthread switches,input_statewhenever an inactive thread receivesTurnCompleted/ThreadClosed,Checked again against the newer prerelease line after upgrading the runtime here to
@openai/codex 0.119.0-alpha.11and syncing a local source checkout to the matching tagrust-v0.119.0-alpha.11.Current read after source inspection: I do not see evidence that this specific stale-restore bug is fixed in
0.119.0-alpha.11.The same critical path is still present in the
rust-v0.119.0-alpha.11source tree:codex-rs/tui/src/app.rsThreadEventStore::push_notification()still clearsactive_turn_idonTurnCompleted, but does not sanitize savedinput_statereplay_thread_snapshot()still restoressnapshot.input_statebefore replaying turns/eventscodex-rs/tui/src/chatwidget.rscapture_thread_input_state()still persists:task_running: self.bottom_pane.is_task_running()agent_turn_running: self.agent_turn_runningrestore_thread_input_state()still restoresself.agent_turn_running = input_state.agent_turn_runningand immediately re-arms:self.turn_sleep_inhibitor.set_turn_running(self.agent_turn_running)self.update_task_running_state()So the stale snapshot / replay ordering hole still appears to exist in this newer alpha.
I also compared
rust-v0.119.0-alpha.3 .. rust-v0.119.0-alpha.11and did not find changes in the obvious/agents-selection files (multi_agents.rs,agent_picker.rs,agent_shortcuts.rs). The main TUI changes in that range touchedapp.rsandchatwidget.rs, but not in a way that obviously closes this restore-order bug.I attempted one targeted
codex-tuitest run on the syncedalpha.11source, but local compilation aborted because the machine is currently out of disk space (No space left on device), so this latest-alpha check is source-level rather than runtime-test-backed.Net: as of
0.119.0-alpha.11, I would still treat this issue as likely open unless there is a fix elsewhere that I missed.One more useful detail from the review before I collapse the duplicate:
There is already a unit test in the TUI suite that encodes part of the problematic behavior:
codex-rs/tui/src/chatwidget/tests/composer_submission.rsrestore_thread_input_state_syncs_sleep_inhibitor_stateThat test explicitly asserts that restoring a snapshot with
agent_turn_running=trueshould re-enable the sleep inhibitor and keep task-running state.So at least part of the stale-state restoration behavior is currently intentional at the unit-test level, not just an accidental side effect.
What I still did not find is a regression test for the important opposite case:
/agentsI think that missing regression test is part of why this bug path could survive across releases.
Version matrix / review scope for this report, to make attribution clearer:
Versions actually involved in my investigation
Runtime artifact when the bug was observed
@openai/codex 0.118.0/usr/bin/codex->/usr/lib/node_modules/@openai/codex/bin/codex.jscodex --versionreported0.118.0Source trees I reviewed
HEADat the time of the first source inspection:c1d18ceb6f22ae3acd67bbd6badad0f475b31dfc0.0.00.118.xrelease-tagged tree I compared against:rust-v0.118.0-alpha.3a5ea426b9a867ec64a41fa2d956bae552036cd4d@openai/codex 0.119.0-alpha.11rust-v0.119.0-alpha.11e169c915824307eef6c175b7f28fb381da853ef0Current read on related issues
This issue seems to sit at the intersection of two existing bug families rather than being a perfect duplicate of any single older report:
/agents/ parent-thread restoration family:#11472is very close in spirit: switching to a sub-agent view and then mishandling what happens when that child thread ends#15723= background subprocesses/subagents do not wake the caller on completion#16386= after reconnect, parent no longer resumes when child completes#15920= sub-agent completion can improperly terminate the parent turn#14318= older race around async subagent notification landing in the wrong parent lifecycle context#12033,#14303,#14314,#15461#12645So my current best framing is:
#16904is not just another generic background-terminal issue,/agentsthread-switch + subagent-completion lifecycle bug that can leave the TUI in a contradictory restored state after the parent turn has already completed.If that framing is right, the most useful regression coverage would likely be an integration test that combines:
/agentsShort triage note after checking the nearby reports:
This does not look like a clean duplicate of any single older issue.
#15920is the closest match for the "subagent completion can improperly end the parent turn" part.#11472is the closest match for the/agentsthread-restore path.#14318is the closest older race around async subagent notification vs parent turn lifecycle.#14314/#12645are the closest symptom family for stale waiting/busy TUI state after completion.What seems specific here is the combination:
/agentsSo this currently looks more like an intersection bug across
/agentsrestore logic and async subagent-completion cleanup than just a generic background-terminal issue.I also re-checked the relevant TUI source path against
0.119.0-alpha.11and still do not see obvious evidence that this specific restore-order / stale-snapshot path is fixed there.Related macOS/Kitty report filed at #19331.
The symptom I saw is a similar stale TUI/busy-state family, but without tmux/subagent switching as the main trigger: Kitty still had a live Codex process, while the rollout JSONL had already ended with
event_msg.task_complete, and the original TUI was non-reactive. Environment: Codex CLI 0.124.0, macOS 26.4.1 arm64, Kitty 0.46.1.