Codex desktop thread remains active/inProgress after interrupted turn with no agent output

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

What version of Codex are you using?

Codex Desktop App, version not captured from the app UI during diagnosis.

Which platform are you using?

macOS desktop app.

What happened?

A Codex desktop thread became stuck in an active state with the latest turn marked inProgress, but there was no visible agent response, no tool call, and no command output.

The affected workspace was:

/Users/wangerpan/Desktop/星瞰

That workspace is not a git repository.

Affected thread details observed through Codex thread inspection:

sourceThreadId: 019ece63-2408-7d62-ad54-092a7a6365a7
stuckTurnId:    019ecfb5-8858-73e0-abca-a29f8496ca00
userMessage:    为啥卡住了
threadStatus:   active
turnStatus:     inProgress
completedAt:    null
durationMs:     null

The immediately previous turn had been interrupted:

previousTurnId: 019ecfb1-d488-7601-8c87-18c99db0b4e7
previousStatus: interrupted
previousDurationMs: 238320

After that interrupted turn, the next user message remained stuck in progress and the thread did not produce an assistant message, an error, or any tool activity.

Steps to reproduce

This is from a real session; exact minimal reproduction is not yet confirmed.

  1. Use Codex Desktop App on macOS.
  2. Work in a projectless or non-git workspace.
  3. Have a long-running turn interrupted.
  4. Send a follow-up user message in the same thread.
  5. Observe that the thread can remain active with the new turn inProgress, while no assistant output/tool calls/errors appear.

Expected behavior

After an interrupted turn, the thread should recover cleanly. A subsequent user message should either:

  • start a normal assistant response,
  • show a clear error, or
  • expose a user-visible way to stop/retry the stuck turn.

Actual behavior

The thread stayed active / inProgress indefinitely with no visible progress and no error.

Recovery attempts

Attempted handoff_thread on the stuck thread, but it failed because the source workspace is not a git repository:

The source thread workspace is not a git repository.

A same-directory fork_thread worked as a workaround:

newThreadId: 019ecfba-ae2b-7b73-bb84-ff8a760a335f

The fork became idle and contained completed history only, avoiding the unfinished stuck turn. The original stuck thread was archived to avoid continued use.

Additional context

This looks like the app/thread scheduler did not fully release or recover state after the interrupted turn. It would help if non-git/projectless threads had a reliable force-stop/retry path, or if interrupted turns could not leave a subsequent user turn permanently in progress.

View original on GitHub ↗

6 Comments

wangerpan · 1 month ago

Additional observation: the same-directory fork workaround did not fully resolve the problem.

The forked thread initially appeared idle:

forkedThreadId: 019ecfba-ae2b-7b73-bb84-ff8a760a335f

After sending a normal follow-up message in that fork, it also became stuck:

newStuckTurnId: 019ecfc0-7b7b-7863-ba1e-a747a19a146f
threadStatus:   active
turnStatus:     inProgress
completedAt:    null
durationMs:     null

Again, there was no assistant message, no tool call, and no surfaced error.

The forked thread also still showed older abnormal turn records from the source thread, including an interrupted turn with completedAt: null and another older inProgress turn. So same-directory fork may not be a reliable recovery path when the source thread already has inconsistent unfinished turn state.

github-actions[bot] contributor · 1 month ago

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

  • #27960

Powered by Codex Action

m13v · 1 month ago

the fix everyone reaches for here is a force-stop/retry button, but the variable that actually decides recoverability is whether thread state is durable on its own. the tell is handoff_thread failing because the workspace isn't a git repo: recovery is bolted to the repo, not the session, so a projectless thread has nowhere to land after an interrupted turn. fork worked precisely because it copies completed history instead of trying to resume the poisoned turn. the axis isn't stop vs retry UI, it's whether the session is the unit of durability or the git tree is.

m13v · 1 month ago

we ended up making the session itself the durable unit, survives restart and forks copy completed history instead of resuming a turn: https://fazm.ai/r/nh5e7t2a

dashitongzhi · 16 days ago

I am seeing the same failure mode repeatedly in Codex Desktop, with a recent concrete case that matches this issue closely.

Environment:

  • Codex Desktop: 26.623.101652 / build 4674
  • macOS: 27.0 build 26A5368g
  • Model/provider setup: custom OpenAI-compatible provider using the Responses API. I realize that may be a variable, but the Desktop state desync described below appears to be client-visible regardless of provider behavior.

Recent stuck thread:

threadId: 019f2dd9-e1ba-77b0-8e1a-a07a0e7cbec7
title:    分析两篇博客
status:   active
cwd:      local git repository

latestTurnId: 019f2de4-370f-7982-9bd5-25ce529ccb26
turnStatus:   inProgress
startedAt:    2026-07-05 00:09:18 +08:00
completedAt:  null
error:        null

The latest turn only emitted a few commentary messages saying it was going to create a Markdown document under docs/. After that there was no final answer and no visible tool result. I checked the repository state separately:

git status --short     # clean
docs/                  # not created
related analysis docs  # not created

So the agent never reached the file-writing step, but the Desktop/thread metadata still reports the thread as active and the turn as inProgress. This creates a zombie-running state: from the UI it looks like it may still be working, but there is no progress and no error surfaced.

This has also been happening frequently in longer/heavier Desktop threads. The recurring patterns I have observed are:

  • image-heavy sessions where generated images or view_image outputs are embedded into the session JSONL, causing sessions to grow very large;
  • long-running threads resumed across multiple days with many tool calls and compacted history;
  • after interruption or stream failure, the visible thread state remains active/inProgress instead of being marked failed, interrupted, or recoverable.

For image-heavy cases, related issues appear to be:

But the specific problem here is the state lifecycle: even when the backend/model/tool chain stops before any side effect, Codex Desktop can leave the turn in inProgress with no error and no completion.

Expected behavior:

  • If the stream disconnects, the provider request fails, or the worker dies before completing the turn, the turn should eventually transition to an explicit failed/interrupted/stale state.
  • The UI should offer a clear retry/resume path rather than keeping the thread apparently active forever.
  • Ideally the thread inspector/status should expose the last heartbeat or worker liveness so users can distinguish "still running" from "zombie inProgress".

Actual behavior:

  • Thread remains active.
  • Latest turn remains inProgress.
  • completedAt stays null.
  • No error is surfaced.
  • No tool/file side effect happened after the last commentary message.

This makes recovery ambiguous and causes users to wait on a thread that has likely already stopped.

surlymochan · 14 days ago

I hit the same failure mode again in Codex Desktop, including after forking the stuck thread.

Environment:

  • Codex Desktop: 26.623.101652 / build 4674
  • macOS: 26.5.1 build 25F80
  • Workspace: local git worktree

Observed original stuck thread:

threadId: 019f2e1e-b18a-7ea3-bfd8-1d2a2d30f2b9
title:    多agent
status:   active

latestTurnId: 019f3699-fb1b-75c1-8d48-f0f152b28a33
turnStatus:   inProgress
completedAt:  null
error:        null
items:        []

Immediately before that, the user sent 继续; that turn was interrupted after about 58s. Then the next in-progress turn existed with no user-visible assistant output, no tool call, no command output, and no surfaced error.

I then forked the thread and saw the same pattern in the fork:

forkedThreadId: 019f36a0-feb2-7721-a94b-6352b31bcc88
title:          多agent
status:         active

latestTurnId: 019f36a1-2808-78b3-880d-f0f152b28a33
turnStatus:   inProgress
completedAt:  null
error:        null
items:        [userMessage: "继续"]

So the fork did not provide a reliable recovery path. It copied the completed history, but after sending a simple follow-up (继续) the fork also remained active/inProgress without any assistant response or tool activity.

Additional context from this case:

  • The source thread was long-running and image-heavy, with browser screenshots/generated image outputs in history.
  • The source thread had multiple interrupted turns before the stuck active turn.
  • The underlying project was not blocked at the build level: a separate shell check of the same worktree showed npm run build completed successfully. Some tests were failing due to normal project-level unfinished work, but the Codex Desktop issue happened before any assistant/tool execution in the latest turn, so this looks like a Desktop/session lifecycle problem rather than a project command hang.

Expected behavior:

  • A forked thread should either recover cleanly and start a new assistant response, or surface an explicit failed/stale/interrupted state.
  • A turn with no assistant/tool activity should not remain indefinitely active/inProgress after the worker has stopped or failed to start.
  • The UI/thread inspector should expose enough liveness state to distinguish real running work from a zombie turn.

Actual behavior:

  • Original thread stayed active with an empty in-progress turn.
  • Forked thread also stayed active after a simple follow-up, with only the user message recorded.
  • No assistant message, tool call, command output, or error was surfaced in either case.