Codex search/grep tool processes run indefinitely with no timeout — orphaned rg floods network FS (Lustre/NFS)
Open 💬 5 comments Opened Aug 10, 2026 by Molaison
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
Bug Description
Codex search/grep tools (launched by codex app-server --listen unix:// in code_mode_host mode) can run indefinitely with no timeout and no automatic kill — orphaned rg processes keep burning CPU/network I/O for 1+ hours on large projects (observed 46 min and 87 min).
On a cluster where the project lives on a network filesystem (Lustre/NFS), each file read is a network round-trip, so an unbounded recursive search produces sustained high-frequency small reads (measured ~72 RPC/s of 1-page reads at the Lustre client) and floods the storage network.
Steps to Reproduce
- Run
codex app-server(code_mode_host) against a large project tree on a network filesystem (e.g. Lustre). - Have the model issue a grep/search tool call (e.g.
rg -n -C 3 PATTERN .orrg -n "a|b|c" <project_root>). - Without any explicit cancellation (close the session, or the request originates from app-server without a
cancellation_token), observe:
- The
rgprocess keeps running for many minutes/hours. psshows it orphaned undercodex app-server(PID chain:rg → codex app-server → sh → systemd).- No
terminate/cancel is ever issued.
Observed on codex-cli 0.147.0:
rg -n "济南|jinan|..." /xcfhome/llyu/project/FBP_binder/prot_design2ran 46 minutes (started 09:40, still alive at 10:26) before eventually finishing.rg -n -C 3 LKQQLREYIRWEEAARNLLGLIE .ran 87+ minutes (still alive at time of writing).
Expected Behavior
- A server-side hard timeout on tool executions (search/grep/exec) — e.g. 60–120 s — after which the child process is killed automatically, even if no client cancellation arrives.
- Or: app-server (long-lived daemon) should reap/track children it spawned and kill them when the originating request/session is gone.
Actual Behavior
- No timeout exists in the search/exec path.
- Cancellation is client-owned only:
codex-rs/app-server/src/request_processors/search.rs:cancellation_tokenis optional —None => Arc::new(AtomicBool::new(false))means "never cancel".codex-rs/exec-server/src/server/handler.rs:terminate()is a passthrough — it is only invoked if the client explicitly calls it.codex-rs/file-search/src/lib.rs: the walker checks the cancel flag only everyCHECK_INTERVAL = 1024entries.- When the requesting session dies/disconnects, nothing kills the spawned child — it orphans under the long-lived
codex app-serverprocess.
Environment
- OS: RHEL 8 (kernel 4.18), x86_64
- Codex CLI: 0.147.0 (npm global)
- Filesystem: Lustre (network FS); also reproduces the general NFS-family slowness
- Home dir on network storage (no local SSD for project data)
Error Output
No error — the processes simply never exit:
$ ps -eo pid,etime,time,args | grep 'rg -n'
1417145 zpzeng 01:26:58 00:04:40 rg -n -C 3 LKQQLREYIRWEEAARNLLGLIE|LKQQLREY .
# ^ still running after 1h26m, parent = codex app-server (long-lived daemon)
$ cat /proc/fs/lustre/osc/*/rpc_stats # client-side evidence of the small-read storm
1-page read RPCs: ~72/s sustained during the search
Additional Context
- Workaround used: manual
killof orphaned rg processes + a watchdog cron that kills search processes exceeding N minutes. - Related code:
codex-rs/app-server/src/fuzzy_file_search.rs(no timeout, cancel-only),codex-rs/exec-server/src/server/handler.rs(terminate is passthrough),codex-rs/file-search/src/lib.rs(cancel check every 1024 entries,follow_links(true),hidden(false)). - The
follow_links(true)+hidden(false)defaults also expand search scope on large trees (symlinked output dirs), compounding the problem.
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Seconded. And this issue causes the codex to run increasingly slower as the number of orphaned tool calls accumulates. I had an instance where a task ran for 2h+ instead of the typical 20min as a result of this.
Your PID chain (
rg → codex app-server → sh) places this in the code-mode cell lifecycle, and it connects to two adjacent reports worth triaging together. In code mode,execthat outlives its yield deadline is parked as a cell by design — the process keeps running and the model is expected towaiton it later (mechanism detailed in #38495). What's missing is any ownership tie-in: nothing kills a cell when the turn that created it completes, the session goes away, or nowaitever comes back — so an expensivergover a network FS simply runs to natural completion, 87+ minutes in your case. (#38093 is the third face of the same lifecycle gap: termination/completion status vs actual child state.)Fix shapes, compatible with the cell model:
rgsupports bounded output; the shell/code-mode prompt guidance could steer toward--max-count/path scoping on network filesystems, but 1–2 are the structural fix.Note plain (non-code-mode) exec already kills at
timeout_ms— the gap is specifically cells, which is why this only bites app-server/code_mode_host deployments like your cluster.Additional reproduction: native Windows Codex Desktop on a local SSD
I reproduced the same underlying failure mode in the native Windows Codex Desktop app, without WSL and without a network filesystem. This case also shows that multiple overlapping code-mode search cells can saturate a local workstation.
Environment
26.818.5345.019045.6456, x6415.1.0-c features.code_mode_host=true app-serverPrivate usernames, query terms, and workspace paths are redacted below.
Observed process tree
Codex launched two overlapping searches with this general shape:
One invocation searched the complete aggregate workspace. The other searched two large subtrees inside it. Each
rg.exehad 13 threads.The downstream
Select-Object -First 500only bounds emitted results. When fewer than 500 matches exist, it does not bound the filesystem scan, sorgstill traverses the complete requested scope.Resource impact
The two searches ran concurrently for more than 20 minutes.
An instantaneous Task Manager sample showed:
| Process | CPU | Memory | Disk read |
|---|---:|---:|---:|
|
rg.exeA | 32.7% | 2,744 MB | 1,675.5 MB/s ||
rg.exeB | 32.0% | 2,474 MB | 1,784.2 MB/s |Additional sampling showed:
The aggregate workspace root did not have a single ignore file covering every subtree. It contained multiple full project trees, dependency/SDK directories, generated content, archives, and hidden directories. The commands used
--hiddenand excluded onlyLibrary,Temp,obj, andbin.Expected behavior
Codex should:
waitoccurs.--hiddensearches by default and apply safe exclusions for VCS, dependencies, generated output, archives, caches, and SDKs.Actual behavior
No effective timeout, throttling, overlap detection, or resource budget was applied. Two model-issued searches were allowed to consume most of the machine's CPU and SSD bandwidth for an extended period.
This does not appear to be a ripgrep defect:
rgwas executing the broad searches it was given. The suspected defect is in Codex search orchestration and code-mode cell lifecycle management.Related Windows reports: #7572 and #34264.
Additional Linux reproduction on codex-cli 0.149.1
I reproduced the same code-mode cell lifecycle failure on Linux with local ext4/LVM storage (not Lustre/NFS). This also appears to interact with the nested-result visibility problem described in #32411.
Environment
0.149.1(npm)5.4.x, x86_64The command was launched from a code-mode
execcell through a nestedexec_commandwithyield_time_ms: 30000. The script only emittedresult.output, so after 30.2 seconds the outer cell renderedScript completedwith empty output while the returnedsession_idwas not surfaced:Observed result
rgand downstreamheadsurvived after their shell/owning cell was gone and were reparented to PID 1.rgremained alive for about 89 minutes until manually terminated./proc/<pid>/ioshowed approximately 1.19 TB inread_bytes.rgPID terminated it.This confirms the issue still reproduces on
0.149.1and can saturate local block storage, not only network filesystems.Expected behavior
session_id, the owning cell should retain/surface that handle and keep lifecycle ownership.result.outputis emitted.No raw session logs, usernames, hostnames, private paths, query terms, or project identifiers are included.