You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[App/Windows/WSL] git-init-watcher retries permanent EISDIR on workspace root every five seconds #32695
Codex Desktop 26.707.8168.0 on Windows 11 repeatedly retries a Windows-side file watcher after receiving EISDIR for a WSL2 workspace root. The retry interval eventually settles at approximately five seconds and continues while the application remains open.
Related to #32183, but this reproduces without using the in-app browser and appears to involve the workspace-level git-init-watcher rather than only the browser-related git-repo-watcher path.
Sanitized observation
Workspace form:
\wsl$<distro>home<user><workspace>
Observed behavior:
Windows Node fs.watch against the WSL workspace root returns EISDIR.
Watching the nested Git repository root produces the same result.
The behavior also reproduces through the equivalent \wsl.localhost path and a mapped drive.
The git-init-watcher retries after failure, with exponential delay capped at approximately five seconds.
One application run recorded 121 failures in about ten minutes.
Historical application logs contained more than 17,000 failures from the same error family.
Git and repository operations performed inside WSL itself continue to work normally.
User names, repository names, task identifiers, full paths, and raw logs have been omitted.
Steps to reproduce
On Windows 11 with WSL2, open a Git workspace located under the Linux home directory in Codex Desktop.
Use a workspace path exposed through \wsl$ or \wsl.localhost.
Leave the desktop application open.
Inspect the Codex application logs for git-init-watcher or file-watch failures.
Actual behavior
A permanent EISDIR watcher failure enters an effectively unbounded retry loop, producing repeated work and log churn approximately every five seconds.
Expected behavior
Detect unsupported or permanent watcher errors such as EISDIR.
Fall back to bounded polling when appropriate, or disable that watcher after a small number of failures.
Adding another independent reproduction of this same behavior.
Environment:
Codex Desktop/MSIX: OpenAI.Codex 26.707.9981.0
Runtime reported in logs: 26.707.72221
Codex CLI: 0.144.2
Windows 11, build 10.0.26200.8875, x64
WSL 2.7.10, Ubuntu 24.04, kernel 6.18.33.2
Agent configured to run in WSL2; workspace on ext4 under /home, with no Git repository initialized
Observed behavior:
The Windows-side process attempts to watch a path shaped like \\wsl$\Ubuntu\home\<wsl-user>\workspace\<workspace>.
fs.watch repeatedly fails with EISDIR.
The retry occurs approximately every 5 seconds, at roughly 12 warnings per minute — consistent with the ~121 failures in ~10 minutes already reported in this issue.
I don't consider the following to be fixes, only workarounds: running git init just to silence the watcher; keeping an artificial Windows project open; moving projects to Windows; or switching the agent to native Windows mode.
This reinforces the suggestion already noted here: detect the permanent error and apply backoff/disable the watcher, or run the watcher in the same environment as the filesystem (WSL), instead of retrying EISDIR indefinitely. I don't have confirmation this is the root cause — this is only another reproduction of the symptom.
Same defect reproduces on Linux with a different errno — ENOENT instead of EISDIR — which suggests the retry logic, not the platform or the specific error, is the problem.
Environment
App
Codex Desktop for Linux, chatgpt-26.810.52044-1.x86_64 (official RPM)
OS
Fedora 44, GNOME / Wayland, x86_64
Kernel
7.1.8
Workspace
plain local ext4 path — no WSL, no SMB, no mapped drive
Observation
The recorded workspace root simply does not exist on this host (it belongs to a different machine). fs.watch() returns ENOENT, and git-init-watcher retries indefinitely:
[git-init-watcher] Failed to watch workspace root for git init
cwd=/home/<user>/git/<project> errorCode=ENOENT
errorMessage="ENOENT: no such file or directory, watch '/home/<user>/git/<project>'"
at e.evaluateImpl (/usr/lib/chatgpt/resources/app.asar/.vite/build/worker.js:200:24538)
at e.evaluate (/usr/lib/chatgpt/resources/app.asar/.vite/build/worker.js:200:23689)
at e1.readLoop (/usr/lib/chatgpt/resources/app.asar/.vite/build/worker.js:200:37425)
Measured 55 occurrences in 30 minutes (~one every 5s), matching the cap described in the original report. A second independent 1-hour sample showed the same line still dominating the journal. No crash, no coredump — the app is otherwise usable.
Why this may be worth generalising
For this particular stale path, ENOENT never resolved — the directory stayed absent for the life of the process, so retrying could not succeed. ENOENT is not permanent in general (a directory may be created later), so the useful distinction is probably "has not resolved after N attempts" rather than "permanent by errno". Related reports show repeated Git-watcher failures in other environments, though not necessarily in the same watcher or code path: #33764, #29084, and #36961. My Linux observation is specifically git-init-watcher returning ENOENT. A fix that classifies non-transient fs.watch() errors and stops retrying — rather than special-casing WSL or SMB paths — would cover all of them.
On a laptop the cost is a wakeup every 5s for the lifetime of the process, plus journal spam that buries other diagnostics.
Superseded — see my later comment. The config.toml entry below turned out not to be the source: removing it did not stop the loop. The path is restored from persisted session state. The rest of this comment is kept for the record.
Correction to my earlier comment — I said I couldn't find where the stale workspace root came from and guessed at synced account state. That was wrong: I searched the wrong directory. It is plain local config.
The path is a project entry in ~/.codex/config.toml:
That directory does not exist on this host, and git-init-watcher retries fs.watch() against it every ~5s for the life of the process.
Local reproduction (no account state, no WSL/SMB needed)
Quit the app.
Add a [projects."<absolute path that does not exist>"] entry to ~/.codex/config.toml (or add a workspace, then delete the directory).
Start the app and leave it idle.
journalctl --user -f | grep git-init-watcher
The ENOENT line repeats indefinitely, no backoff, no give-up.
Superseded — see my later comment. The config.toml entry turned out not to be the source: removing it did not stop the loop. The path is restored from persisted session state. I have only tested Linux, so I can't say how this behaves on macOS or Windows — which fits this being one watcher bug across platforms (ENOENT here, EISDIR on WSL in the original report) rather than several platform-specific ones.
Trigger isolated on Linux: the stale path survived every configuration-level removal I could find.
Same host as my earlier comments (Fedora 44, chatgpt-26.810.52044-1.x86_64, Wayland). Following the ENOENT loop back, the missing workspace root kept returning after every config-level removal. The evidence points at historical rollout files as the source, though I have not read the code and cannot confirm the mechanism.
That directory no longer exists. After a restart the rows were observed recreated in local_thread_catalog; state_5.sqlite was separately observed to hold the same path, though I did not watch it being repopulated. The two stores were local_thread_catalog in ~/.codex/sqlite/codex-dev.db, and threads in ~/.codex/state_5.sqlite. The thread IDs in local_thread_catalog match the rollout filenames exactly; I did not perform the same check against state_5.sqlite.
Why config-level fixes do not work
Verified in this order, measuring after each step:
Removed the [projects."…"] entry from config.toml → thread persisted.
Launched with cwd /tmp and zero [projects] entries → thread persisted.
Deleted the rows from local_thread_catalog → rows were recreated on next start, and the thread came back heavier than before.
Moved the three rollout files aside and cleared both derived tables → thread went idle and stayed idle across a restart.
Only step 4 held — but note it changed three things at once (moved the rollouts and cleared both stores), so it does not isolate which of them was authoritative. What it does establish is that no configuration-level change I tried was sufficient; clearing persisted state was required.
Measured cost (instantaneous /proc sampling, not ps lifetime averages)
state
app CPU
RSS
Tctl
as installed, settled idle
30.0%
3417 MB
64.9 °C
as installed, peak observed
41.8%
3437 MB
74.8 °C
after moving rollouts aside and clearing both stores
2.0%
2328 MB
58.0 °C
A thread named git inside the main process accounted for 99 of 121 ticks (82% of the main process's work) in one sample, and 242 ticks in a later one. git-init-watcher log lines went from ~55 per 30 min to 0. Across the full mitigation sequence, settled Tctl fell from 64.9 °C to 58.0 °C; the pre-change peak was 74.8 °C. These measurements do not isolate the temperature contribution of each change.
Observed effects associated with the same stale path
The behaviour is consistent with there being no staleness check when the thread index is rebuilt — inferred from observation, not from reading the code. Either way, a cwd that no longer exists should be skipped rather than watched. (I measured CPU in a thread named git and the watcher's log output; I did not instrument what it was reading, so "scanning" would be my assumption rather than a measurement.)
The retry loop already described in this issue is the visible symptom; the CPU burn is the larger cost and is easy to miss because the app remains responsive.
Unrelated but found alongside — the Linux app launches its GPU process with --ozone-platform=x11 on a Wayland session, Under X11, both GetVSyncParametersIfAvailable() failed and Unable to get gpu adapter appeared in the journal and the GPU process measured 17.7% CPU on an idle window. Under Wayland, it measured 6.5%, and both error classes were absent after restart. Happy to open that separately if it is out of scope here.
Edited after posting for accuracy. Two changes worth flagging: (a) the measurement table originally showed a single "as installed" row of 41.8% / 74.8 °C, which was the peak sample rather than the settled idle baseline and overstated the improvement — both pre-cleanup samples are now shown separately; (b) several statements about the app's internals were phrased as fact when they are inferred from observed behaviour, and have been reworded. I have not read the source. The measurements themselves are unchanged.
Further edit: narrowed several statements that outran the evidence — which store was observed being recreated versus merely holding the path, which table had its IDs cross-checked, "scanning" (not instrumented), "free-running" (an inference), and the fact that the final step was a compound intervention rather than an isolated one.
Update from Codex Desktop 26.825.4187.0 on Windows 11 with WSL2/Ubuntu 24.04: the issue persists.
One live sample recorded 299 git-init-watcher/EISDIR failures in 1,312 seconds, settling at roughly one retry every five seconds.
A related path bug persists: after exact offline cwd correction and successful JSON/SQLite integrity checks, Desktop restart regenerates malformed values such as <PACKAGE_ROOT>\app\resources\C:\... or <PACKAGE_ROOT>\app\resources\\wsl$\.... The saved WSL project root itself remains valid.
One affected session also ended in Windows MoAppHang after repeated Chromium GPU-process crash/relaunch failures. The watcher was active concurrently; this does not establish causation.
No OOM, WSL crash, disk exhaustion, task-history/database corruption, workspace modification, or data loss was found.
Please normalize WSL cwd paths, never prefix absolute paths with the package resource directory, stop/back off after deterministic watcher failures, and expose a supported watcher-disable setting.
All user, host, task, and project identifiers are removed; no raw logs are attached.
Activity
github-actions commentedon Jul 13, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
gilmarvitor commentedon Jul 16, 2026
Adding another independent reproduction of this same behavior.
Environment:
Observed behavior:
\\wsl$\Ubuntu\home\<wsl-user>\workspace\<workspace>.fs.watchrepeatedly fails with EISDIR.I don't consider the following to be fixes, only workarounds: running
git initjust to silence the watcher; keeping an artificial Windows project open; moving projects to Windows; or switching the agent to native Windows mode.This reinforces the suggestion already noted here: detect the permanent error and apply backoff/disable the watcher, or run the watcher in the same environment as the filesystem (WSL), instead of retrying EISDIR indefinitely. I don't have confirmation this is the root cause — this is only another reproduction of the symptom.
stephanelepain-hub commentedon Aug 16, 2026
Same defect reproduces on Linux with a different errno —
ENOENTinstead ofEISDIR— which suggests the retry logic, not the platform or the specific error, is the problem.Environment
chatgpt-26.810.52044-1.x86_64(official RPM)Observation
The recorded workspace root simply does not exist on this host (it belongs to a different machine).
fs.watch()returnsENOENT, andgit-init-watcherretries indefinitely:Measured 55 occurrences in 30 minutes (~one every 5s), matching the cap described in the original report. A second independent 1-hour sample showed the same line still dominating the journal. No crash, no coredump — the app is otherwise usable.
Why this may be worth generalising
For this particular stale path,
ENOENTnever resolved — the directory stayed absent for the life of the process, so retrying could not succeed.ENOENTis not permanent in general (a directory may be created later), so the useful distinction is probably "has not resolved after N attempts" rather than "permanent by errno". Related reports show repeated Git-watcher failures in other environments, though not necessarily in the same watcher or code path: #33764, #29084, and #36961. My Linux observation is specificallygit-init-watcherreturningENOENT. A fix that classifies non-transientfs.watch()errors and stops retrying — rather than special-casing WSL or SMB paths — would cover all of them.On a laptop the cost is a wakeup every 5s for the lifetime of the process, plus journal spam that buries other diagnostics.
stephanelepain-hub commentedon Aug 16, 2026
Correction to my earlier comment — I said I couldn't find where the stale workspace root came from and guessed at synced account state. That was wrong: I searched the wrong directory. It is plain local config.
The path is a project entry in
~/.codex/config.toml:That directory does not exist on this host, and
git-init-watcherretriesfs.watch()against it every ~5s for the life of the process.Local reproduction (no account state, no WSL/SMB needed)
[projects."<absolute path that does not exist>"]entry to~/.codex/config.toml(or add a workspace, then delete the directory).journalctl --user -f | grep git-init-watcherThe
ENOENTline repeats indefinitely, no backoff, no give-up.Superseded — see my later comment. The
config.tomlentry turned out not to be the source: removing it did not stop the loop. The path is restored from persisted session state. I have only tested Linux, so I can't say how this behaves on macOS or Windows — which fits this being one watcher bug across platforms (ENOENThere,EISDIRon WSL in the original report) rather than several platform-specific ones.stephanelepain-hub commentedon Aug 16, 2026
Trigger isolated on Linux: the stale path survived every configuration-level removal I could find.
Same host as my earlier comments (Fedora 44,
chatgpt-26.810.52044-1.x86_64, Wayland). Following theENOENTloop back, the missing workspace root kept returning after every config-level removal. The evidence points at historical rollout files as the source, though I have not read the code and cannot confirm the mechanism.Chain of custody
Three rollout files dated 2026-05-01:
each containing:
That directory no longer exists. After a restart the rows were observed recreated in
local_thread_catalog;state_5.sqlitewas separately observed to hold the same path, though I did not watch it being repopulated. The two stores werelocal_thread_catalogin~/.codex/sqlite/codex-dev.db, andthreadsin~/.codex/state_5.sqlite. The thread IDs inlocal_thread_catalogmatch the rollout filenames exactly; I did not perform the same check againststate_5.sqlite.Why config-level fixes do not work
Verified in this order, measuring after each step:
[projects."…"]entry fromconfig.toml→ thread persisted./tmpand zero[projects]entries → thread persisted.local_thread_catalog→ rows were recreated on next start, and the thread came back heavier than before.Only step 4 held — but note it changed three things at once (moved the rollouts and cleared both stores), so it does not isolate which of them was authoritative. What it does establish is that no configuration-level change I tried was sufficient; clearing persisted state was required.
Measured cost (instantaneous
/procsampling, notpslifetime averages)A thread named
gitinside the main process accounted for 99 of 121 ticks (82% of the main process's work) in one sample, and 242 ticks in a later one.git-init-watcherlog lines went from ~55 per 30 min to 0. Across the full mitigation sequence, settled Tctl fell from 64.9 °C to 58.0 °C; the pre-change peak was 74.8 °C. These measurements do not isolate the temperature contribution of each change.Observed effects associated with the same stale path
cwdthat no longer exists should be skipped rather than watched. (I measured CPU in a thread namedgitand the watcher's log output; I did not instrument what it was reading, so "scanning" would be my assumption rather than a measurement.)Unrelated but found alongside — the Linux app launches its GPU process with
--ozone-platform=x11on a Wayland session, Under X11, bothGetVSyncParametersIfAvailable() failedandUnable to get gpu adapterappeared in the journal and the GPU process measured 17.7% CPU on an idle window. Under Wayland, it measured 6.5%, and both error classes were absent after restart. Happy to open that separately if it is out of scope here.Edited after posting for accuracy. Two changes worth flagging: (a) the measurement table originally showed a single "as installed" row of 41.8% / 74.8 °C, which was the peak sample rather than the settled idle baseline and overstated the improvement — both pre-cleanup samples are now shown separately; (b) several statements about the app's internals were phrased as fact when they are inferred from observed behaviour, and have been reworded. I have not read the source. The measurements themselves are unchanged.
Further edit: narrowed several statements that outran the evidence — which store was observed being recreated versus merely holding the path, which table had its IDs cross-checked, "scanning" (not instrumented), "free-running" (an inference), and the fact that the final step was a compound intervention rather than an isolated one.
yunyunFlow commentedon Aug 29, 2026
Update from Codex Desktop
26.825.4187.0on Windows 11 with WSL2/Ubuntu 24.04: the issue persists.git-init-watcher/EISDIRfailures in 1,312 seconds, settling at roughly one retry every five seconds.<PACKAGE_ROOT>\app\resources\C:\...or<PACKAGE_ROOT>\app\resources\\wsl$\.... The saved WSL project root itself remains valid.MoAppHangafter repeated Chromium GPU-process crash/relaunch failures. The watcher was active concurrently; this does not establish causation.No OOM, WSL crash, disk exhaustion, task-history/database corruption, workspace modification, or data loss was found.
Please normalize WSL cwd paths, never prefix absolute paths with the package resource directory, stop/back off after deterministic watcher failures, and expose a supported watcher-disable setting.
All user, host, task, and project identifiers are removed; no raw logs are attached.