Codex Desktop accepts prompt but UI stays stuck in Thinking; Stop fails and turn can become invisible after restart

Open 💬 27 comments Opened May 24, 2026 by winnal
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

Codex Desktop 26.519.41501 (CFBundleVersion 3044)

What subscription do you have?

ChatGPT Pro

What platform is your computer?

macOS 15.6.1 build 24G90
arm64
Apple M1 Max
64 GB RAM

Local app/runtime details observed from the installed app and local Sentry scope:

Bundle id: com.openai.codex
Electron: 42.1.0
Chrome runtime: 148.0.7778.97
Sentry release: codex@26.519.41501

What issue are you seeing?

Codex Desktop intermittently accepts prompts and continues executing backend work, but the Desktop UI loses reliable active-turn/session state. The visible chat can get stuck in Thinking, stop showing progress traces, show stale or missing turn state, or disagree with other UI surfaces about whether work is active.

I would consider this a major/critical issue, not a cosmetic UI bug, because the Desktop UI is the primary observability and control surface for active Codex execution. If trace visibility, active-turn state, and Stop/cancel state desynchronize, the user loses reliable control over active background work.

Important clarification: I have not observed an app crash, renderer crash, or automatic reload before this happens. The app can remain open. The issue appears to be that the renderer-visible session/turn/trace state becomes detached, stale, frozen, missing, or incompletely rehydrated while backend work may still be running.

The core bug:

  • the session/turn appears active or was already visibly active;
  • the UI can be stuck on Thinking;
  • the prompt bar may show an active turn with the Stop button, or in some /goal cases may incorrectly show Send while the goal bar still says the goal is actively pursuing;
  • no new progress traces appear, even while usage may continue decreasing;
  • Stop may be unavailable, misleading, or unresponsive;
  • the UI does not reliably reflect what the backend is actually doing.

This has happened intermittently for roughly a month. It appears random, but it may be more likely with long-running sessions/threads, trace-heavy turns, subagents/tools, compaction, multi-window use, /goal, or after a relogin/auth flow while the app remains open.

Observed failure modes

1. Prompt accepted, UI stuck in Thinking, Stop fails

Basic pattern:

  • I send a prompt.
  • The UI stays stuck on Thinking.
  • Pressing Stop does not stop the turn.
  • I am forced to quit/restart Codex Desktop manually.
  • After restart, the thread can look like my prompt was never sent, or it can show later assistant progress without showing the prompt and/or traces that happened while the UI was stuck.
  • Because the UI makes it look like the prompt was not accepted, I may resend the prompt.
  • Later recovered state proves the original prompt was accepted and the backend/agent had been working the whole time.
  • In previous occurrences, trying to recover by resending the prompt or forking the chat has produced errors along the lines of cannot find session / missing session.
2. Live trace/progress stream disappears

When this bug happens, it is not only the final assistant message or prompt bubble that can be affected. The live trace/progress stream itself can disappear.

Observed variants:

  • stuck Thinking with no traces shown, even though backend work appears to continue;
  • restart can remove traces that were previously visible;
  • for long tasks, the trace stream can randomly reattach later without restart;
  • sending a follow-up prompt has sometimes appeared to unstick/reconnect the trace stream.
3. /goal state split and multi-window rehydration failure

This also appears associated with the /goal feature, and the order of events matters.

Corrected repro order from one occurrence:

  1. I had already started the goal via a prompt in the original window.
  2. The goal was working normally.
  3. The original session UI was showing the prompt/goal progress traces normally.
  4. The prompt box was correctly showing the Stop button, indicating an active turn was running.
  5. Only after that, I tried opening the same session in a new Codex window.
  6. In the new window, the session failed to load or did not finish loading the active session state.
  7. At that point, the already-visible goal/prompt progress traces disappeared in both the new window and the original window.
  8. Both windows then looked as if the active turn/progress had been lost in the chat/trace area, and the prompt box showed Send instead of Stop. However, the goal bar above the prompt box still showed the goal as actively pursuing, not paused or stopped.
  9. After waiting some time, all of the traces that had previously been visible spontaneously appeared again, and the prompt box changed back to Stop.

This is different from the UI simply failing to show traces from the beginning. The traces and active Stop state were visible and working first. Opening the same session in a second window appears to have caused the Desktop UI to lose or detach the already-visible active turn/session state across all windows.

The especially strange part is that different parts of the same UI disagreed about whether work was active: the goal bar still showed an active pursuing-goal state, while the chat/trace area lost the active progress and the prompt box reverted to Send. That suggests a session/turn state split between the goal-level active state and the visible turn/trace/input state.

4. Multi-window active-session monitoring can freeze/detach visible progress across sessions

This is especially concerning when trying to monitor multiple active Codex sessions at once. My goal was to open each active session in its own Desktop window so I could watch each one progress, instead of manually switching between sessions inside a single window.

Observed behavior:

  • one existing window/session had been showing the active turn's persisted traces/progress correctly;
  • after trying to open active sessions in new windows, one of the windows hit the session loading/rehydration issue;
  • after that, visible progress across the sessions/windows appeared to become frozen or detached;
  • all of the sessions I had opened in new windows stopped showing new traces;
  • the backend presumably continued working in the background, but the Desktop UI stopped showing new trace/progress updates;
  • this makes the UI unreliable as a monitor for active work, because it can show no progress even while work may still be running.

This may be connected to very long sessions/threads. These sessions are long-running and trace-heavy, and it is starting to look possible that the app is failing to consistently load or rehydrate long session state unless I completely quit and restart the app.

5. Confirmed active backend usage while UI is frozen

This now appears confirmed as active backend execution with a frozen/detached Desktop UI.

Across multiple active sessions:

  • the UI shows the sessions as Thinking;
  • the prompt box shows the Stop button, indicating an active turn;
  • no new progress traces or activity are visible in the app;
  • the visible state appears frozen;
  • however, my usage dashboard is actively decreasing while this is happening.

That strongly indicates Codex is still executing and using model resources in the background, but the Desktop app is not showing any of the activity, traces, or progress. The issue is not simply that the model stopped or that the turn is idle. The backend appears active while the UI is detached from the running turn state.

Concrete evidence from one occurrence

After restart/recovery, the visible history showed that the original stuck turn had actually continued doing work. It showed status/progress like:

Steered conversation
The previous commit-boundary reviewer handle is not available in this resumed tool session, so there’s no valid approval artifact to consume. A new prompt delta just arrived too; I’m checking it against the already registered EK-DOCS-SYNC delta before I touch any gate state.

Explored 1 file, ran 2 commands
The repeated EK-DOCS-SYNC directive is already represented in the repo-local prompt-delta artifact and successor proof: full docs-state backfill, coverage matrix, canonical docs, and explicit forbidden actions are all present. I’m going to have the commit-boundary reviewer independently verify that duplicate/newest prompt is covered before approving the commit boundary.

Spawned 1 agent

Ran 3 commands
The reviewer is running while I keep my side read-only. One local inspection hit a harmless parsing assumption in VALIDATION.json; I’m not treating that as a repo failure, just as a cue to use the file’s actual schema when refreshing validation later.

The independent commit-boundary gate is approved with no findings, including the repeated EK-DOCS-SYNC directive check. I’m closing that reviewer and recording the gate outcome into repo-local artifacts/state before moving to commit approval.

From the user perspective before the manual restart, the app was simply stuck in Thinking, Stop did not work, and I could not see that work. I resent the prompt because the UI made it appear as if the prompt had not been sent.

Impact

  • Active agent work may continue while the UI appears frozen, stale, or detached. Usage can continue decreasing while no traces/progress are visible.
  • The user cannot reliably observe what Codex is doing.
  • The user cannot reliably determine whether a turn is active, stopped, detached, or invisible.
  • Stop/cancel may be unavailable, misleading, or unresponsive.
  • The visible chat history can be misleading after restart.
  • Duplicate prompts can be created because the original prompt is hidden or appears unsent.
  • Resend/fork recovery paths can hit missing-session style errors.
  • Long-running workflows with subagents/tooling can be duplicated, conflicted, or left running invisibly.
  • Multi-window monitoring of active sessions becomes unreliable because opening/loading one session can appear to freeze or detach visible progress across windows.

For an agentic desktop app that can run tools, spawn agents, and work on repositories, reliable active-turn state, trace visibility, and cancellation are core safety/control surfaces. If those desynchronize, the app becomes unsafe/unreliable for serious long-running work.

What steps can reproduce the bug?

I do not have a deterministic repro yet. Observed patterns:

Basic stuck Thinking / invisible turn path
  1. Use Codex Desktop on macOS during a long-running local workspace thread.
  2. Keep the app open. In some cases this may happen after a relogin/auth flow.
  3. Send a prompt.
  4. The UI remains stuck on Thinking after the prompt.
  5. Try pressing Stop. It does not stop the turn.
  6. Manually quit/restart Codex Desktop.
  7. Reopen the thread.
  8. The prompt/trace may be missing or partially recovered, even though later state shows the agent had been executing the prompt.
  9. If the prompt is resent because it looks unsent, it may later appear as a steered/duplicate continuation, or recovery actions like forking can hit missing-session errors.
/goal / second-window rehydration path
  1. Start a /goal turn via prompt.
  2. Let it run normally in the original window.
  3. Confirm traces/progress are visible and the prompt box shows Stop.
  4. Open the same session in a new Codex Desktop window.
  5. The new window fails to load or does not finish loading the active session state.
  6. Already-visible traces disappear in both windows.
  7. The goal bar still shows actively pursuing, but chat/trace state disappears and the prompt box may show Send instead of Stop.
  8. After waiting, traces may spontaneously reappear and the prompt box may return to Stop.
Multi-window active-session monitoring path
  1. Have multiple long-running active sessions.
  2. Open each session in its own Desktop window to monitor progress.
  3. One or more windows fail to load/rehydrate active session state completely.
  4. Visible progress traces across sessions/windows stop updating or appear frozen/detached.
  5. Backend work may still be running, but the UI no longer reliably shows progression.

Local state observations

Sanitized local state observations from the same app session:

  • Local Sentry scope had active app-state snapshots during the session.
  • Breadcrumb counts included:
  • method=turn/start: 2
  • method=turn/steer: 1
  • Item not found in turn state: 2
  • app_state_snapshot: 26
  • App-state snapshots repeatedly included active/streaming mismatch indicators:
  • thread_count_active: 4
  • thread_count_streaming_owner: 3
  • thread_count_streaming_without_role: 1
  • thread_count_streaming_without_active_runtime: 2-3
  • thread_count_with_inflight_turn: 1-2
  • inflight_turn_count: 1-2
  • browser_use_active_turn_route_count: 1-2

I am not claiming this is the same as reports involving a confirmed crash/reload, placeholder rebinding, or thousands of Item not found in turn state errors. The observed user-facing bug here is: prompt accepted and executed, UI stuck in Thinking or active-turn state becomes detached/frozen, Stop/Send state becomes misleading, and visible prompt/trace/session state is missing or misleading until restart or later rehydration.

What is the expected behavior?

If Codex Desktop accepts a prompt or continues a goal, it should durably and visibly associate that backend turn with the originating thread/session across windows.

Expected behavior:

  • The user prompt should remain visible after submission.
  • Assistant progress, tool calls, subagent activity, and traces should stream reliably while work is active.
  • Active turn state should be consistent between the goal bar, chat/trace area, and prompt box.
  • Opening the same session in another window should not detach or clear traces in the original window.
  • Opening multiple active sessions in separate windows should not freeze visible progress streams across sessions.
  • Stop should reliably cancel the backend turn or show why it cannot.
  • If the renderer/thread UI loses ownership of an active backend turn, the app should show a recovery/reattach state instead of generic stuck Thinking or misleading Send/Stop state.
  • Resending or forking should not produce missing-session errors or duplicate/steered turns because of hidden accepted prompts.
  • Long sessions should either load/rehydrate reliably or show an explicit loading/recovery state that does not erase visible active traces.

Additional information

This seems like a session/turn state ownership or trace-stream subscription problem between the Desktop UI and the backend/app-server. The backend appears to accept and execute prompts, but the UI can fail to show the submitted prompt, active stream, tool traces, goal/turn alignment, and cancellation state.

The main issue is not one exact repro path. It shows up through /goal, multi-window session opening, long-running turns, restarts, and sometimes follow-up prompts. The common failure is that the Desktop UI does not reliably and consistently show the real active state, progress, and trace stream for running sessions, especially long sessions.

View original on GitHub ↗

27 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #24263
  • #24217
  • #23292

Powered by Codex Action

winnal · 3 months ago

Additional detail about the visible trace/progress stream:

When this bug happens, it is not only the final assistant message or prompt bubble that can be affected. The live trace/progress stream itself can disappear.

Observed variants:

  1. Stuck Thinking with no traces shown

Sometimes after I send a prompt, the UI gets stuck on Thinking and none of the traces/steps/progress are shown, even though the backend appears to be working.

  1. Restart can remove traces that were previously visible

After manually quitting/restarting the app, the reopened thread can be missing parts of the thread that were previously present. In particular, if I send a prompt during an active turn and the app bugs out, then after restart the entire previous turn's traces can be gone from the visible UI.

  1. Trace stream can randomly reattach later without restart

For long tasks, Codex normally shows progress/traces continuously during the turn. When the bug happens, it may show only Thinking and nothing afterward for a long time. If I wait an extended time, the UI can randomly resume showing traces again before the turn finishes.

  1. Follow-up prompt can sometimes unstick the trace stream

In at least one occurrence, I sent another follow-up prompt while it was stuck, and that appeared to unstick the UI: the thinking/progress traces started showing again.

This makes the bug feel like a live turn trace/session subscription or ownership desync. The backend work can continue, but the renderer-visible thread can stop receiving or rendering the active turn's trace events. Later, the UI may reattach spontaneously or after another prompt, but restart can also cause previously visible traces to disappear from the reconstructed thread view.

MisterRound · 3 months ago

This is essentially the same thing I'm seeing in my issue https://github.com/openai/codex/issues/24263

jshaofa-ui · 3 months ago

---
title: "Codex Desktop accepts prompt but UI stays stuck in Thinking; Stop fails and turn can become invisible after restart"
issue: https://github.com/openai/codex/issues/24287
repo: openai/codex
type: bug-fix
area: app, session, app-server
competition: low (3 comments, but no solution proposed)
quote: $1,500-$2,500
---

Complete Solution

Root Cause Analysis

This is a session/turn state ownership desync between the Codex Desktop UI (Electron renderer) and the backend/app-server. The core issue:

  1. Prompt accepted but UI loses ownership: The backend accepts and executes the prompt, but the UI's turn subscription fails to receive or render the prompt bubble, assistant progress, and tool traces
  2. Stop fails: The Stop button can't cancel because the UI doesn't have a valid reference to the active turn
  3. State invisible after restart: The thread's prompt/trace state is not durably associated with the originating thread, so restart shows misleading or missing history

Key evidence from Sentry breadcrumbs:

  • thread_count_active: 4 but thread_count_streaming_owner: 3 — one active thread has no streaming owner
  • thread_count_streaming_without_role: 1 — a streaming thread has no role assigned
  • thread_count_streaming_without_active_runtime: 2-3 — streaming threads with no active runtime
  • Item not found in turn state: 2 — turn state lookup failures

Fix — Three-Layer Approach

Layer 1 — Turn ownership reconciliation (primary fix):

// In app-server's turn management (Rust)
// When a prompt is accepted, ensure the turn is durably linked to its thread
// BEFORE any streaming begins

fn handle_prompt(thread_id: &str, prompt: Prompt) -> Result<TurnId> {
    // 1. Create turn record in the thread's durable store FIRST
    let turn = Turn::new(thread_id, prompt);
    self.store.save_turn(&turn)?;  // Durable write before streaming
    
    // 2. THEN start the backend execution
    let turn_id = turn.id.clone();
    self.backend.execute(turn_id.clone(), prompt);
    
    // 3. Subscribe the UI to the turn stream
    self.ui.subscribe(thread_id, turn_id.clone());
    
    Ok(turn_id)
}

Layer 2 — Stop/cancel reliability:

// In the Desktop UI's Stop handler
async handleStop() {
    // Get the current turn_id from the durable store, not just in-memory state
    const turnId = await this.store.getActiveTurnId(this.threadId);
    if (!turnId) {
        // Show recovery UI instead of silently failing
        this.showRecoveryState("No active turn found. Checking backend state...");
        const backendTurn = await this.appServer.findActiveTurn(this.threadId);
        if (backendTurn) {
            // Re-attach UI to the backend turn
            await this.reattachToTurn(backendTurn);
        }
        return;
    }
    await this.appServer.cancelTurn(turnId);
}

Layer 3 — Recovery/reattach state:

// When the UI detects it has lost ownership of an active turn
async detectAndRecover() {
    const localActive = await this.store.getActiveTurns(this.threadId);
    const backendActive = await this.appServer.getActiveTurns(this.threadId);
    
    // Find turns that exist in backend but not in UI
    const orphanedTurns = backendActive.filter(
        bt => !localActive.some(lt => lt.id === bt.id)
    );
    
    for (const orphaned of orphanedTurns) {
        // Show a recovery banner
        this.showRecoveryBanner({
            type: 'orphaned_turn',
            turnId: orphaned.id,
            message: 'An active turn was found that is not visible in the UI. Click to re-attach.',
            action: () => this.reattachToTurn(orphaned)
        });
    }
}

Testing Strategy

  • Unit test: verify turn is durably stored before streaming begins
  • Integration test: simulate UI crash during active turn, verify recovery on restart
  • Edge case: verify Stop works even when UI has lost turn ownership
  • Regression test: verify normal turn flow (prompt → stream → response) is unaffected

Files to Change

  1. codex-rs/app-server-daemon/src/turn_manager.rs — add durable turn storage before streaming
  2. app/src/turn-state-manager.ts — add recovery/reattach logic
  3. app/src/stop-handler.ts — use durable turn reference instead of in-memory

Impact

  • Prompts are durably associated with their originating thread
  • Stop button reliably cancels the backend turn or shows recovery state
  • Orphaned turns are detected and recoverable after restart
  • No duplicate prompts from invisible accepted prompts
winnal · 3 months ago

Additional detail: this also appears associated with the /goal feature, and the order of events matters.

Corrected repro order:

  1. I had already started the goal via a prompt in the original window.
  2. The goal was working normally.
  3. The original session UI was showing the prompt/goal progress traces normally.
  4. The prompt box was correctly showing the Stop button, indicating an active turn was running.
  5. Only after that, I tried opening the same session in a new Codex window.
  6. In the new window, the session failed to load or did not finish loading the active session state.
  7. At that point, the already-visible goal/prompt progress traces disappeared in both the new window and the original window.
  8. Both windows then looked as if the active turn/progress had been lost in the chat/trace area, and the prompt box showed Send instead of Stop. However, the goal bar above the prompt box still showed the goal as actively pursuing, not paused or stopped.
  9. After waiting some time, all of the traces that had previously been visible spontaneously appeared again, and the prompt box changed back to Stop.

This is different from the UI simply failing to show traces from the beginning. The traces and active Stop state were visible and working first. Opening the same session in a second window appears to have caused the desktop UI to lose or detach the already-visible active turn/session state across all windows.

The especially strange part is that different parts of the same UI disagreed about whether work was active: the goal bar still showed an active pursuing-goal state, while the chat/trace area lost the active progress and the prompt box reverted to Send. That suggests a session/turn state split between the goal-level active state and the visible turn/trace/input state.

That makes the session state shown in the UI feel temporary or incompletely rehydrated: the persisted/streaming state seems to disappear from the renderer until whatever session/turn binding or trace stream loading finishes again. When it reattaches, the missing traces reappear as if they had been there the whole time.

This still does not look like an app crash. The app remains open. The visible session state becomes inconsistent across windows, then later rehydrates.

winnal · 3 months ago

Additional impact / multi-window observation:

This is especially concerning when trying to monitor multiple active Codex sessions at once. My goal was to open each active session in its own Desktop window so I could watch each one progress, instead of manually switching between sessions inside a single window.

What I observed:

  • one existing window/session had been showing the active turn's persisted traces/progress correctly;
  • after trying to open active sessions in new windows, one of the windows hit the session loading/rehydration issue;
  • after that, visible progress across the sessions/windows appeared to become frozen or detached;
  • I could no longer reliably see further progress traces in the UI;
  • the backend presumably continued working in the background, but the Desktop UI stopped showing new trace/progress updates;
  • this makes the UI unreliable as a monitor for active work, because it can show no progress even while work may still be running.

This may be connected to very long sessions/threads. These sessions are long-running and trace-heavy, and it is starting to look possible that the app is failing to consistently load or rehydrate long session state unless I completely quit and restart the app.

The concerning failure mode is: opening the same or other active sessions in additional windows can appear to detach or freeze the visible active-turn state across windows, leaving the user unable to tell what is currently running or whether Stop would work.

winnal · 3 months ago

Core failure mode clarification:

As of now, all of the sessions I opened in new windows are no longer showing any new traces. This is the main problem behind the different variants I have described.

The core bug is:

  • the session/turn appears active;
  • the UI can be stuck on Thinking;
  • the prompt bar may show an active turn with the Stop button;
  • but no new progress traces appear;
  • the UI does not reliably respond or reflect what the backend is actually doing.

So the issue is broader than one exact repro path. It shows up through /goal, multi-window session opening, long-running turns, restarts, and sometimes follow-up prompts, but the common failure is that the Desktop UI does not reliably and consistently show the real active state, progress, and trace stream for running sessions, especially long sessions.

In other words: the app can have an active backend turn, but the renderer-visible session state/trace stream becomes detached, frozen, missing, or stale. That makes the app UI unreliable for monitoring active Codex work and for knowing whether Stop will actually work.

winnal · 3 months ago

Severity note:

I would consider this a major/critical issue, not a cosmetic UI bug.

Reason: the Desktop UI is the user's primary control and observability surface for active Codex execution. When it stops showing real progress traces, shows inconsistent active state, or leaves the user unsure whether Stop will work, the user loses reliable control over active background work.

Impact:

  • active agent work may continue while the UI appears frozen or stale;
  • the user cannot reliably observe what Codex is doing;
  • the user cannot reliably determine whether a turn is active, stopped, or detached;
  • the Stop control may be unavailable, misleading, or unresponsive;
  • users may resend prompts or open/fork sessions because the UI makes the active state unclear;
  • long-running repo workflows can be duplicated, conflicted, or left running invisibly.

For an agentic desktop app that can run tools, spawn agents, and work on repositories, reliable active-turn state, trace visibility, and cancellation are core safety/control surfaces. If those desynchronize, the app becomes unsafe/unreliable for serious long-running work.

winnal · 3 months ago

Additional confirmation: this now appears confirmed as active backend execution with a frozen/detached Desktop UI.

Across multiple active sessions:

  • the UI shows the sessions as Thinking;
  • the prompt box shows the Stop button, indicating an active turn;
  • no new progress traces or activity are visible in the app;
  • the visible state appears frozen;
  • however, my usage dashboard is actively decreasing while this is happening.

That strongly indicates Codex is still executing and using model resources in the background, but the Desktop app is not showing any of the activity, traces, or progress. The issue is not simply that the model stopped or that the turn is idle. The backend appears active while the UI is detached from the running turn state.

This is the core reliability problem: active work can continue invisibly while the app UI is stuck and provides no usable observability.

grizzithal · 3 months ago

I’m seeing the same failure mode on Codex Desktop as well.

In my case, the important part is that the UI appears detached from an active backend turn rather than the turn simply being idle. The thread can remain stuck on Thinking, progress traces stop updating, and Stop either does not work or does not appear to reflect the actual backend state. I have also seen cases where the prompt/turn appears missing after restart even though later state suggests the backend accepted and continued executing it.

My observed pattern:

  • Codex accepts a prompt in an existing long-running thread.
  • The UI stays on Thinking with no new trace/progress updates.
  • The prompt box may still show Stop, but pressing it does not reliably cancel the work.
  • Restarting the app may show missing or partial turn history.
  • In some cases, usage continues decreasing while the UI shows no visible activity, which suggests backend execution is still active.
  • Sending a follow-up prompt or waiting can sometimes cause the trace stream to reattach.

This is especially risky because the UI makes it unclear whether a turn is actually running, stopped, detached, or invisible. From a user-control perspective, the problematic behavior is not just stale rendering; it is loss of reliable observability and cancellation for active agent work.

I can provide app version/platform details and local diagnostics if helpful. Are there specific logs, Sentry scope values, or local state files that would be most useful to capture the next time this happens?

Kai-Watson · 2 months ago

same here

peterw · 2 months ago

same issue

martinmclee · 2 months ago

Follow-up for this issue
I can still reproduce this with:

Codex App version: 26.608.12217
Codex CLI version: 0.139.0
This still matches the same failure pattern:

UI stays in Thinking during an active turn
progress/trace output is not updating reliably
Stop may not appear to stop execution
behavior looks stale or detached from actual backend activity

martinmclee · 2 months ago

Extra context on the same stuck thread: the session was already in Plan mode before restart by Codex Desktop update. After restarting to 26.608.12217, UI now shows Plan mode as off, but the thread remains stuck in the Plan execution path and Plan mode cannot be turned off. Workaround that unblocks me: switching reasoning level and continuing there; that allowed the thread to proceed. This seems like a session/state desync, not an actual mode toggle mismatch.

franksong2702 · 1 month ago

Tracking this from a downstream app-server client / Codex Mobile integration because it matches the same user-visible failure mode: a submitted message can leave the client showing an active/Thinking state while the durable thread state does not reliably show the new turn.

Sanitized local signal from a recent occurrence:

  • The client submit endpoint received the message and returned a new turnId from turn/start.
  • A follow-up durable read still showed the previous latest completed turn, with no matching newly submitted turn visible.
  • The local session JSONL/rollout did not contain the returned turnId or the client submission id after the submit response.
  • A local fallback cache then projected the returned turnId as active, which made the Mobile UI look like the session was still thinking even though persistent state had not advanced.

That makes this feel like the same class of session/turn ownership and durable-state desync described here: the user-facing client cannot safely distinguish "accepted and running" from "accepted-looking but not materialized". We have added a downstream guard that verifies the submitted turn is visible via bounded thread state before showing it as active; subscribing here for the upstream fix.

michaliskon · 1 month ago

I reproduced this on Windows across the VS Code extension and Codex Desktop using the same local thread.
A /goal was actively running in VS Code. After opening another chat/window and returning, both VS Code and Desktop showed stale conversation state ending several messages earlier. The goal bar still showed the goal as pursuing, but the visible turn appeared stopped.
The local rollout JSONL retained the full history and continued growing, confirming that backend reasoning and tool activity were still running. At the same time, the exposed thread state reported notLoaded and the latest turn as interrupted.
This suggests the rehydration/state-synchronization problem also affects cross-surface VS Code ↔ Desktop usage, not only multiple Desktop windows.

bbingz · 1 month ago

Analysis (community)

Residual analysis for #24287 (prompt accepted but UI stuck in Thinking; Stop unresponsive; turn invisible after restart) under docs/contributing.md — invitation-only. No unsolicited PR.

Root-cause hypothesis

The renderer/backend desync described here has a structural OSS contributor in codex-rs: turn-terminal events are gated behind an unbounded rollout flush, and everything downstream — the Stop reply, the agent status, and the unload GC — depends on those terminal events arriving. One stalled rollout-file write (Windows file-system filters/Defender; Windows has no file-write timeout) therefore converts a transient IO stall into a permanently stuck, unstoppable, un-GC-able thread. Verified at HEAD 4c43465133:

1. TurnComplete is emitted only after flush_rollout() — a timeout-less wait on a serial writer. In the task wrapper (codex-rs/core/src/tasks/mod.rs:402-435), task.run() completes, then line 415 does a bare sess.flush_rollout().await — no timeout, no cancellation select — and only afterwards reaches on_task_finished (:427-431), the sole production emitter of EventMsg::TurnComplete (construction at tasks/mod.rs:803-813). The flush chain bottoms out at Recorder::flush (codex-rs/rollout/src/recorder.rs:965-980), which sends RolloutCmd::Flush into a bounded 256-slot channel (recorder.rs:892) and does rx.await on a oneshot ack — again no timeout. The single serial writer (recorder.rs:899, writer loop :1774-1804) does write_all + flush per line (recorder.rs:1922-1928); tokio::fs is spawn_blocking-backed, so an OS-level write stall never returns an error — the recovery path only reacts to errors, the ack is never sent, and the wrapper parks at tasks/mod.rs:415 forever. Meanwhile events fan out persist-before-deliver (core/src/session/mod.rs:2072-2082): tool results produced before the stall reached the UI, which is exactly this issue's "tool calls visibly succeed but the turn never finishes."

2. Stop is double-blocked by the same writer. Op::Interrupt → handle_task_abort (tasks/mod.rs:880-917) cancels the token, waits a 100 ms grace (:67), aborts the wrapper handle — then, to persist the interrupted-turn marker (default-enabled), awaits flush_rollout().await again at :914 before emitting TurnAborted. Same stalled writer, same park. At the app-server layer the turn/interrupt JSON-RPC is additionally never answered immediately: the request id is pushed into pending_interrupts (app-server/src/request_processors/turn_processor.rs:1445) and Ok(None) is returned (:1460-1461); the only non-test drain sites are TurnComplete (bespoke_event_handling.rs:189) and TurnAborted (:1094), via respond_to_pending_interrupts (:1554-1568). No terminal event ⇒ the Stop request hangs forever. This queue/drain defect is distinct from the approval-oneshot freeze tracked in #32862 — different queue, different response type.

3. The stuck thread is then pinned for the whole session. With no terminal event, agent_status_from_event (core/src/agent/status.rs:6-21) never leaves Running, and the 30-minute idle-unload GC explicitly skips Running threads (app-server/src/request_processors/thread_lifecycle.rs:5, :356-359). Worse, if the core event stream ends mid-turn, the listener exit path (thread_lifecycle.rs:386-389 → thread_state.rs:134-141 clear_listener) resets the listener without draining pending_interrupts or synthesizing any terminal notification — so even a restart/reconnect of the app-server leaves the queued Stop unanswered.

4. The post-restart symptom is fully explained by OSS replay code. michaliskon's observation — "rollout JSONL retained the full history and continued growing… thread state reported notLoaded and the latest turn as interrupted" — matches: a persisted TurnAborted replays to TurnStatus::Interrupted in handle_turn_aborted (app-server-protocol/src/protocol/thread_history.rs:1204-1235), and after a full restart every persisted thread defaults to NotLoaded (thread_status.rs:366-369, 430-432). (Path note: thread_history.rs lives under app-server-protocol/src/protocol/, not app-server/src/.) The ghost-turn meanwhile can keep appending to the rollout after the "abort," matching "JSONL kept growing."

5. Adjacent, same-lifecycle (distinct defect, mentioned for triage only): if the interrupt lands while ActiveTurn{task: None} — the window between active_turn being set (tasks/mod.rs:494-507) and start_task assigning the handle — abort_all_tasks sets aborted_turn=false and emits no TurnAborted and no event at all (tasks/mod.rs:509-523); the queued interrupt reply then only resolves when the turn eventually completes. That is the "Stop press silently ignored, turn runs to completion" variant reported here.

What this analysis deliberately does NOT cover: the renderer-side subscription desync, double-window freeze, and renderer timeouts are closed Desktop Electron; the resume-side compaction growth that makes long crash-resume loops progressively worse (Legacy full-file reload + unbounded Compacted(replacement_history) accumulation) is analyzed separately in my comment on #33493 (<https://github.com/openai/codex/issues/33493#issuecomment-5079148354>) — linked here because long /goal sessions hit both, but the fix surface differs.

Fix outline (all OSS): (a) wrap both flush awaits (tasks/mod.rs:415, :914) in tokio::time::timeout (2–5 s); on timeout emit TurnComplete/TurnAborted anyway with a Warning event ("transcript save is lagging; retrying in background") — converts "stuck forever" into "turn ends, save retries"; (b) bound Recorder::flush itself (recorder.rs:965-980) with a timeout on rx.await, plus a writer-health watchdog that surfaces a terminal_failure when the serial writer makes no progress, so senders fail fast instead of backpressuring the whole event fanout; (c) drain pending_interrupts with an error/cancel response on listener exit and connection close, and synthesize a terminal notification (e.g. TurnStatus::Failed) when the core event stream ends mid-turn; (d) close the ActiveTurn{task: None} ghost-stop window by emitting TurnAborted (or an explicit error to queued interrupts) instead of silently dropping the abort.

Fail-first tests I'd want before any merge: (1) writer ack never arrives → TurnComplete still emits within the timeout, turn status Completed, turn/interrupt reply resolves (today: all three hang); (2) interrupt during the flush stall → TurnAborted within the timeout rather than never; (3) interrupt in the task: None window produces a visible terminal/error event, not silence; (4) listener drop with queued interrupts drains them with an error response (today: dropped). (1)–(3) are achievable with a fault-injected rollout writer; (4) is app-server-only. Happy to draft the test patch if a maintainer wants it. No unsolicited PR from me.

kumag36 · 27 days ago

Additional Windows/Desktop data point (2026-08-01 JST):

  • Codex Desktop version: 26.721.11231.0 on Windows.
  • In a multi-thread project workflow, a background worker thread completed work but its terminal callback did not become visible/deliver to the coordinating thread while the target worker thread was not open.
  • This was observed repeatedly: opening/focusing that exact worker task was immediately followed by the pending callback being delivered. The instruction was not resent, and no duplicate execution was observed.
  • In an earlier occurrence, a standard cross-thread send remained unanswered for 24,303,264 ms. The PC had not slept, the same app-server process was still alive, and the stopped model transport count observed at that point was zero. Delivery completed immediately after Desktop/thread resume. The initial connection-loss trigger remains unverified.
  • This looks stronger than ordinary rendering lag because the coordinating workflow remained waiting for the callback until the target task was foregrounded, although the exact backend/renderer boundary is still unverified.

Expected behavior: a terminal callback/event should be delivered independently of whether the target thread is currently visible or focused, or the client should automatically resume/reconcile the thread without requiring UI interaction.

Temporary recovery observed locally: do not resend the instruction; foreground the exact target thread once. Fully restarting Desktop is a secondary recovery, but it risks losing visible trace continuity. I can provide sanitized timestamps/log excerpts privately if useful; no repository or conversation content is included here.

Roland0511 · 18 days ago

Additional sanitized diagnostic data point from a case where this affected not only the renderer, but also the thread-reading projection and subsequent model-visible context.

Environment

  • Codex Desktop: 26.803.41515 (build 6321)
  • Bundled/installed CLI: codex-cli 0.147.0
  • macOS 26.5.2 (build 25F84), arm64
  • Thread ID: 019fa27e-f0ea-7f71-9c78-de349bfd3365

Sanitized timeline (UTC)

  1. 10:39:38.241 — ordinary turn A started, turn ID 019feb41-aee3-7923-a41a-1249815e9d33.
  2. 10:39:38.361 — the user message for turn A was persisted in the rollout.
  3. 10:40:12.460 — context compaction occurred while turn A was active.
  • The compacted record had 221 replacement-history items.
  • It included the active user message and a compaction summary marker.
  • This was not the boot-preamble-only / empty-summary failure described in #36642.
  1. 10:40:20.843 — the assistant's final answer for turn A was persisted.
  2. 10:40:21.211 — task_complete was persisted for turn A.
  3. 10:43:57.609 — ordinary follow-up turn B started, turn ID 019feb45-a3e9-76e3-aa84-a0cba38f7546.
  4. 10:43:58.704 — the user message for turn B was persisted.
  5. 10:44:04.141 — an assistant commentary message for turn B was persisted.
  6. 10:44:22.062 — turn_aborted was persisted for turn B.

After turn B was aborted:

  • Desktop no longer displayed either the already-completed turn A or the interrupted turn B.
  • A current read_thread call also omitted both turn IDs and jumped from an earlier completed turn to later turns.
  • Subsequent model-visible context behaved as if those messages had never happened; the agent could not recognize the missing question/answer until the raw rollout was inspected manually.
  • Fully quitting and restarting Codex Desktop did not restore the missing turns.
  • The raw rollout JSONL still contains the exact user and assistant message records, task_complete for A, and turn_aborted for B.
  • There is no thread_rolled_back event around this sequence.

Conversation text, repository names, and local paths are intentionally omitted.

This appears more severe than a trace-only rendering issue: an abort of the current turn seems able to make a previously completed turn disappear from the canonical thread projection used by all three consumers:

  1. Desktop transcript
  2. thread-reading API
  3. model-context reconstruction

The durable rollout remains intact, so the likely failure boundary is replay/indexing/canonical-turn reconstruction, possibly involving the combination of compaction inside turn A followed by aborting turn B.

Expected invariant: aborting turn B must not remove completed turn A, and UI, thread APIs, and model-visible context should converge on the durable rollout history.

tonyzhu163 · 17 days ago

This seriously hurts usability of running long-horizon tasks. I'm not sure how this has been in place for several months now?

ByteYue · 16 days ago

Adding a sanitized data point involving Codex Desktop, subagent-heavy work, and a remote Linux app-server.

Environment

  • Codex Desktop client: 26.803.41515
  • Remote app-server: 0.147.0
  • App-server managed by a systemd user service
  • Multiple Codex tasks open
  • Long/tool-heavy subagent turn with compilation activity
  • Repository names, branches, user paths, prompts, thread IDs, and turn IDs omitted

I observed two related state-reconciliation failure modes.

Case A: app-server restart while subagent work existed

The app-server service was terminated by systemd-oomd and automatically restarted.

After the restart:

  • the old OS processes belonging to the subagent were no longer present
  • the affected child/subagent session was not present in the new app-server process's active state
  • the Desktop UI could still retain an apparently active/running representation
  • Stop/retry controls did not reliably correspond to a live backend turn or process

The worktree and file modifications remained intact, but the live execution handle did not survive the service restart. The problematic part was not the loss of the OS process itself; that is expected after the whole service cgroup is killed. The problematic part was that the client did not reliably reconcile the stale running state into an explicit interrupted, failed, or recoverable state.

Case B: stale running UI without an app-server restart

A later occurrence happened after the restart, while the app-server remained continuously healthy.

The Desktop UI showed a long-running command as active. At the time of inspection:

  • no matching compilation process existed in the Linux process table
  • server-side turn telemetry had reached a terminal model decision:
  • model_needs_follow_up=false
  • has_pending_input=false
  • needs_follow_up=false
  • the Desktop UI still presented the command/turn as running
  • a new user message remained visibly queued
  • pressing Stop generated a process/kill request, but there was no matching OS process to terminate
  • the service restart counter did not change during this occurrence

A few minutes later:

  • the queued message was accepted as a new turn on the same child thread
  • the long context triggered automatic compaction at approximately 255k cumulative tokens
  • the follow-up turn subsequently emitted turn/completed

This suggests that the backend/thread remained usable while the Desktop view temporarily retained stale active-turn/process state.

Possible contributing pressure

During the same general interval, another Desktop connection generated a burst of Git process/spawn and process/exited events, similar to the behavior reported in #26812. I did not capture an explicit slow-connection disconnect warning for this exact occurrence, so I am not claiming that outbound backpressure is the proven trigger. It may simply be an event-load or client-state-rehydration amplifier.

Expected recovery behavior

After a disconnect, app-server restart, missing terminal event, or unknown process handle, the client should ideally:

  1. Re-read the authoritative thread/turn status.
  2. Clear cached active-turn and running-process state if the server has no corresponding active object.
  3. Mark the previous execution as interrupted, lost, or detached.
  4. Make Stop idempotent and surface "process/turn no longer exists" rather than remaining stuck.
  5. Allow queued input to start a new turn or present an explicit reattach/retry action.
  6. Avoid silently retaining an active UI state solely because turn/completed was not observed on the original connection.

I am not claiming that this is necessarily the same internal trigger as every reproduction in this issue. The user-facing state split, however, is very similar: backend state and Desktop-visible active/Stop/queue state can disagree for an extended period.

I can provide a redacted event timeline and systemd journal summary if that would help triage.

harryshawk · 13 days ago

Additional macOS Codex Desktop evidence matching this issue:

Affected task/thread IDs:

  • 019fc246-0b10-75e2-acfe-d666c96cd246
  • 019fa4ea-80fe-79b3-afc0-689bb52f12ba

Observed behavior:

  • The thread remains visually active as though it is still thinking.
  • A newly submitted message enters the queue and is shown as a steered message, but it remains there and never starts.
  • Pressing the black square End/Stop button does nothing; the apparent active turn cannot be ended.
  • This affects more than one thread.
  • Both the Mac and the Codex/ChatGPT application were recently fully power-cycled/restarted, but the condition remains.

The UI therefore presents an active-turn state while neither End/Stop nor queued-message delivery works. This is not resolved by restarting the machine or application. Screenshots captured on 2026-08-14 show the queued Steer card and the unresponsive End/Stop control.

harryshawk · 13 days ago

Additional macOS Codex Desktop reproduction (2026-08-14):

Before restarting Codex, thread 019fa4ea-80fe-79b3-afc0-689bb52f12ba is responsive, while thread 019fc246-0b10-75e2-acfe-d666c96cd246 is not responding. This is thread-specific within the same running app rather than a general connectivity failure. Codex has not yet been restarted, so recovery has not yet been tested.

AdiedX · 12 days ago

I'm seeing what appears to be a closely related issue in the current ChatGPT/Codex desktop app on macOS.

Environment:

  • MacBook Pro 16-inch (2021)
  • Apple M1 Pro
  • 32 GB RAM
  • macOS Tahoe 26.2
  • ChatGPT Plus
  • Issue became very noticeable within the last ~24 hours, after a recent ChatGPT desktop update

Thread session IDs:
01a00856-d54e-7221-8669-c91b61e33172
01a00348-8da0-7593-924d-02b0fdf60f6c

The most obvious repro for me is starting work in a new desktop window/session:

  1. Open a new ChatGPT desktop window.
  2. Submit a substantial prompt, often using Goal mode / agentic work.
  3. The turn starts, but then abruptly stops or loses forward progress even though the task is clearly unfinished.
  4. If I paste/send the same prompt again, or otherwise nudge the conversation, it starts working normally.

This is happening frequently enough that I now expect to have to submit the initial request twice.

I've completely deleted the ChatGPT app, downloaded a fresh copy, and reinstalled it. The behavior persists.

I'm also seeing a related failure after context compaction: the agent stops instead of continuing the active task and requires another user message to resume.

This did not occur regularly for me previously and seems to have become significantly worse following the recent desktop update.

ChatGPT desktop build number Version 26.810.52044

shleder · 11 days ago

Your “prompt accepted/backend still working, UI stuck in Thinking, restart/recovery shows hidden work” report is directly relevant to a failure class I’m field-testing with codex-rescue. The tool is an experimental local-first session fsck/recovery utility; it does not fix Desktop UI rehydration, but it can check what durable evidence actually exists after the UI loses the active turn.

If you still have one of the affected local sessions, please start with:

pipx install codex-rescue
codex-rescue sessions
codex-rescue doctor --latest

If it reports an interrupted/unfinished state and you want to test the handoff path:

codex-rescue salvage --latest --fork
codex-rescue verify <rescue-id>

The source rollout is intended to remain untouched and unknown side effects are not auto-replayed. Sanitized output/versions/exit codes only, please; no raw rollouts/DBs, prompts, credentials, or private paths. Repo: https://github.com/shleder/codex-rescue

prashantbhamu · 9 days ago

I reproduced this on Windows, and the failure is especially clear because I could compare the frozen Desktop renderer with the same task's persisted/backend state from another task.

Environment

  • Codex Desktop: 26.814.5167.0 (x64)
  • Windows: 10.0.26200.0 x64
  • Model shown in the affected task: GPT-5.6 Sol, medium reasoning

Sequence

  1. Started a new task from ChatGPT real-time voice.
  2. Ended the real-time voice session.
  3. The task window remained on Thinking with a spinner/Stop button and did not show further progress.
  4. Inspecting the same task from a separate Codex task showed that the underlying turns were already completed, while the affected window still looked active.
  5. Clicking Stop produced Failed to stop chat. Archiving also failed.
  6. Fully restarting Codex Desktop rehydrated the task correctly: Thinking disappeared and the previously missing final response became visible.
  7. Without using voice again, I typed a keyboard-only follow-up in that same recovered task.
  8. The bug reproduced: the affected window showed only the submitted prompt plus Thinking, with no progress messages.
  9. From a separate task, the backend reported a genuine inProgress turn and exposed multiple assistant commentary updates plus completed/in-progress tool executions. None of those updates appeared in the affected window.
  10. Sending a stop instruction through the underlying task service completed the turn and changed its backend status to idle, while the affected renderer had not provided reliable visibility/control.

Important distinction
This produced both variants:

  • stale UI says active after the backend has already completed (Stop and archive fail);
  • backend is genuinely active and producing commentary/tool progress, but the Desktop window does not stream or display those updates.

The second variant reproduced with keyboard-only input after restarting the app. Voice was only involved in the original creation of the task, suggesting that a voice-originated task may retain a broken renderer/stream subscription across later text turns.

Expected
The task window should reattach to the persisted/live turn stream, show commentary and tool progress, clear Thinking on completion, and keep Stop/archive availability consistent with backend state.

prashantbhamu · 9 days ago

Additional evidence after a full Windows PC restart:

  • The affected task still could not be archived from the Desktop UI.
  • The task's persisted status was fully completed/not running.
  • Attempting to archive the same task through the underlying Codex task service also failed, with the exact error:
failed to archive session: thread-store internal error: failed to archive thread:
The system cannot find the file specified. (os error 2)

I then checked the obvious local targets. The session JSONL file exists, the ~/.codex/archived_sessions directory exists, and the task working directory exists. This suggests a stale or mismatched internal path/reference in the thread store rather than the main session file actually being absent.

A full application restart and a full PC restart both failed to repair the archive operation. This extends the impact beyond renderer/stream desynchronization: the affected voice-originated task appears to retain corrupted or inconsistent thread-store metadata that prevents lifecycle operations such as archive.