[Windows][Desktop 26.803.5235.0] Git review cancellation spams Electron "Error: write EOF"; disable toggle is hidden

Open 💬 4 comments Opened Aug 11, 2026 by DanielleFong

What version of the Codex App are you using?

26.803.5235.0 (Microsoft Store/MSIX)

  • Bundled Chromium/Electron annotation: 151.0.7922.76
  • Codex CLI doctor: 0.147.0-alpha.6.5, windows-x86_64

Platform

Windows 11 Pro 10.0.26100 x64

Issue

Codex Desktop repeatedly opens a native Electron modal:

A JavaScript error occurred in the main process

Uncaught Exception:
Error: write EOF
    at WriteWrap.onWriteComplete [as oncomplete]
    (node:internal/stream_base_commons:87:19)

The main app and app-server remain alive, but modal dialogs recur while Git-based Review live queries are repeatedly canceled and retried in a large local repository.

Reproduction

  1. Open a large/dirty local Git repository in Codex Desktop.
  2. Keep a local task running while the Review panel/background review-model queries refresh.
  3. A background git status ... --untracked-files=no may hit the 60-second timeout.
  4. The Git watcher restarts and review-summary / branch-diff-stats commands enter a cancel/retry loop.
  5. Electron main-process write EOF dialogs begin recurring.

Correlated log evidence

At one reproduction:

  • 19:18:41.895Z: background git status timed out after 60012 ms
  • 19:19:10.068Z: [git-repo-watcher] Starting git repo watcher
  • 19:19:11 local capture: Electron write EOF modal
  • follow-on review commands repeatedly canceled; some returned about 741376 bytes stdout and 13-133 KiB stderr

Git itself is not generally hung: the same status command later completed in about 1.0s and fsmonitor reported healthy. The failure appears to be in Windows child-process cancellation/pipe handling plus live-query retry behavior.

Hidden workaround regression

The installed app bundle defines:

  • key: git-review-mode
  • values: full / last-turn-only
  • UI label: Disable Git-Based Review
  • description: it disables Unstaged/Staged/Branch to avoid Git operations

Settings search finds Disable Git-Based Review, but Settings → Git does not render the toggle. The settings component appears feature-gated out.

Adding this to config.toml is accepted by strict config validation:

[desktop]
git-review-mode = "last-turn-only"

However, the running desktop process does not hot-reload it; a restart is required.

Expected behavior

  • Canceling or timing out Git children should handle EOF/EPIPE without an uncaught Electron exception/modal.
  • Live Git queries should back off instead of entering a cancel/retry loop.
  • Disable Git-Based Review should be visible if it is indexed by Settings search.
  • git-review-mode changes should hot-reload, or the UI should state that a restart is required.

Privacy

Local username, repository path/content, and project media are omitted. Sanitized log excerpts and screenshots are available if needed.

View original on GitHub ↗

4 Comments

DanielleFong · 16 days ago

Confirmed recurrence after a full process restart with the workaround loaded.

  • Before restart, Electron main PID was 4220.
  • After restart, Electron main PID was 39496 (created 15:38:27 local); app-server PID 57360.
  • The app-rewritten config still contains:

``toml
[desktop]
git-review-mode = "last-turn-only"
``

  • The installed bundle confirms this is the correct desktop config key path.

Despite that setting, the new session continued issuing source=review_model requestKind=review-summary commands. The post-restart log shows:

  • 22:57:39Z through 22:57:45Z: git diff ... --find-renames --raw --no-abbrev --numstat -z canceled/retried about once per second.
  • 22:58:17Z through 22:58:33Z: another rapid sequence of canceled git diff and git status --renames --porcelain=v1 -z --untracked-files=no calls.
  • 22:59:53.612Z: the review-summary git status completed with durationMs=60010, failureReason=timed_out, timedOut=true.
  • The Electron main-process modal Error: write EOF recurred afterward.

Therefore last-turn-only is not an effective workaround in Desktop 26.803.5235.0. It may reduce activity initially, but it does not stop Review-model Git operations or prevent the cancellation/pipe crash.

Current requested fixes remain:

  1. Catch/ignore expected EOF/EPIPE on canceled child-process streams.
  2. Stop the one-second Review query retry storm and add backoff.
  3. Make Disable Git-Based Review visible and ensure it actually disables Review-model Git operations.
  4. Provide a supported emergency kill switch for all background Git integration.
lwz20210407 · 12 days ago

Still reproducing on a newer build than the one in the report, with the same modal and the same stack. Adding a detail I haven't seen named in this thread (or in #38360 / #38202 / #35985): a specific stdin-fed git child that gets aborted mid-write.

Environment

| | |
|---|---|
| Codex Desktop | OpenAI.Codex_26.803.10989.0_x64__2p2nqsd0c76g0 (MSIX), release 26.803.81509 |
| Bundled Chromium/Electron | 151.0.7922.76 (same annotation as this report) |
| OS | Windows 11 Pro 10.0.26200 x64 |
| Workspace | local git repo with ~64,000 untracked files |

The cancel/retry loop is confirmed here

One main-process log for a single day records, across git.command.complete entries: 11× failureReason=canceled, 3× timed_out, 2× nonzero_exit — including git status at 60014 ms and git ls-files --others --exclude-standard at 60001 ms, both requestKind=status-summary. That matches step 3–4 of the repro in the original report.

The candidate writer: git hash-object --stdin-paths

Two entries stand out, because this is the only git invocation in the flow that receives its input over stdin (paths are streamed into the child, one per line):

2026-08-16T02:53:51.446Z warning [git] git.command.complete
  command="git -c safe.bareRepository=explicit -c core.hooksPath=NUL -c core.fsmonitor= hash-object --stdin-paths"
  aborted=true failureReason=canceled durationMs=2803 exitCode=null timedOut=false
  requestKind=review-summary source=local_conversation_git_actions
  stdoutBytes=12915 stderrBytes=0 cwd=D:\<workspace>

2026-08-16T06:13:13.589Z warning [git] git.command.complete
  command="... hash-object --stdin-paths"
  aborted=true failureReason=canceled durationMs=3829 exitCode=null timedOut=false
  requestKind=review-summary source=local_conversation_git_actions
  stdoutBytes=12915 stderrBytes=0

stdoutBytes=12915 together with aborted=true means git had already streamed ~13 KB of hashes back when the cancellation landed — i.e. the parent was still feeding paths into the child's stdin at kill time.

Note the requestKind=review-summary source=local_conversation_git_actions tag: same review-summary path this report calls out.

Why this fits write EOF specifically

On Windows, writing to a pipe whose read end has gone away yields ERROR_BROKEN_PIPE, which libuv maps to UV_EOF — so Node surfaces it as write EOF, not EPIPE. A socket peer-reset would normally read EPIPE / ECONNRESET instead (as in the browser-use native-pipe path in #38360). A killed child's stdin is therefore a much better fit for the exact string in this modal, plus its WriteWrap.onWriteComplete frame.

Caveat — this is correlation, not a captured trace

The app's own logger contains no EOF / EPIPE / uncaught-exception entry anywhere in its log tree. The exception goes straight to Electron's default uncaughtException handler → dialog.showErrorBox, so there is no logged timestamp to line up against the modal. I'm offering hash-object --stdin-paths as a lead because it is the one place in this flow where the parent holds a writable handle to a child it also cancels — not as a proven trace.

Suggested fix / check

Wherever hash-object --stdin-paths is driven, the child's stdin should get an 'error' listener before the cancellation path can kill the process, and write() calls should be guarded on destroyed / writableEnded. Worth auditing every other stdin-fed child spawned by the git layer the same way, since any of them can lose its reader mid-write during a cancel.

One divergence from the reports in this thread

Contrary to "the modal blocks the app" (reported here and in #38360), on my install the main process keeps running with the dialog open — it is still logging normally while the un-dismissed modal sits on screen. So the blocking behaviour appears to vary, which may be worth accounting for when reproducing.

legrch · 11 days ago

I can confirm the hidden Settings-search mismatch independently on macOS.

Environment

  • ChatGPT/Codex desktop: 26.810.52044 (build 6662)
  • macOS: 26.6.1 (25G76)

Reproduction

  1. Open Settings.
  2. Search for turn.
  3. Settings search indexes the Git result Only show “Last Turn” in the Review panel…
  4. Open the Git settings page.
  5. No corresponding control is rendered.

This reproduces the hidden/feature-gated settings UI problem described in this issue, independently of the Windows write EOF failure. Search should not index a control that is unavailable to the current account/build, or the Git page should expose the control and explain its rollout status.

I also added the undocumented setting below locally:

[desktop]
git-review-mode = "last-turn-only"

The config parses successfully, but the desktop app requires a full restart before its UI behavior can be verified. I can follow up after testing. A screenshot of the Settings search mismatch is available and contains no private repository data.

legrch · 11 days ago

Follow-up after testing the undocumented setting on macOS with a full desktop-app restart:

[desktop]
git-review-mode = "last-turn-only"

Environment:

  • ChatGPT/Codex desktop: 26.810.52044 (build 6662)
  • macOS: 26.6.1 (25G76)

Observed behavior:

  • The Review pane does not open automatically; Ctrl+Shift+G is still required.
  • After the restart, opening the pane manually selects Last Turn.
  • The other scopes remain visible and selectable: Uncommitted, Unstaged, Staged, Committed, and Branch.

So, in this macOS build, the key behaves like a persistent/default scope preference rather than a literal “Last Turn only” UI lock. This test does not establish whether the setting suppresses background Review-model Git operations.

The remaining supported-UX request is tracked in #38875: expose the default scope as a normal preference and optionally open/restore the Review pane automatically.