openai / openai/codex

thread/delete can wait forever on a thread loaded by the same app-server

Open
#41,645 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app-server bug CLI
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of Codex CLI is running?

codex-cli 0.148.0 (official stable standalone build)

What subscription do you have?

ChatGPT-backed Codex; exact plan is not relevant to this local app-server lifecycle bug.

Which model were you using?

Not applicable. The failure occurs before any model turn.

What platform is your computer?

macOS 26.6.1, Darwin arm64.

What terminal emulator and version are you using (if applicable)?

Noninteractive CLI invocation against the managed standalone Unix-socket app-server.

Codex doctor report

Not included because authentication, model access, and network state are not in the failing path. CLI, managed binary, and live app-server all reported 0.148.0; the runtime ownership checker was otherwise green.

What issue are you seeing?

codex delete <UUID> --force can wait forever when the target archived thread is already loaded by the same long-running standalone app-server.

In the observed run, the client remained connected to the Unix control socket but produced no response for several minutes. Both the client and app-server were at 0% CPU with no rollout I/O; the target SQLite row and rollout file remained unchanged. Interrupting the client did not cancel the server-side request: a later lifecycle RPC also timed out until the standalone app-server was restarted.

No thread content, UUID, database, or raw log is attached.

What steps can reproduce the bug?
  1. Start the managed standalone app-server on unix:// and keep it running.
  2. Resume/load a persisted thread through that app-server.
  3. Archive the thread while the same server retains its loaded writer/lifecycle state.
  4. From another client connected to that managed server, run codex delete <UUID> --force.
  5. Observe that deletion can wait indefinitely instead of completing or returning an active-writer conflict.

The local discriminator ruled out descendants, an external rollout file owner, database corruption, and rollout size. Two unloaded archived threads deleted normally before the loaded target. After an official codex app-server daemon restart, deep inspection showed loaded=0, and deleting the previously blocked target succeeded immediately.

What is the expected behavior?

Deleting a thread loaded by the same app-server should either prepare/unload it and complete, or fail quickly with a bounded, actionable conflict. It should not wait indefinitely, and disconnecting the requesting client should not leave a permanently stuck server-side request.

Additional information

The 0.148.0 source appears to explain the self-wait:

  • thread_delete holds the thread-list state permit while awaiting thread_delete_response.
  • LocalThreadStore::delete_threads awaits lock_lifecycle(thread_id) and then live_writer_locks.lock(thread_id).
  • prepare_thread_for_delete / prepare_thread_for_removal is called only after those locks are acquired.

For a thread whose writer/lifecycle lock is already held by the same app-server, preparation therefore cannot run until after the non-reentrant lock wait. The thread_delete.rs blob is identical between tag rust-v0.148.0 and current main at the time of this report (c83140d7ad700599a3a0aeb4df40197c9b226759).

Relevant source:

The bounded local recovery was to restart only the standalone app-server from an independent Desktop-hosted task, then retry the official delete command. No SQLite edit or raw rollout deletion was used.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with codex-rs/app-server/src/request_processors/thread_delete.rs and codex-rs/thread-store/src/local/delete_thread.rs, tracing the state permit, lifecycle lock, and live-writer lock while reproducing deletion of a loaded archived thread. Compare the behavior with an unloaded thread and verify that deletion completes or returns a bounded, actionable conflict without leaving later lifecycle RPCs stuck.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, sqlite
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.