VS Code extension sandbox leaves empty .git directory, causing Git discovery CPU spin

Open 💬 2 comments Opened Jun 17, 2026 by hkwi

Summary

When using the Codex VS Code extension in a non-Git workspace on a Linux remote environment, Codex sandbox setup appears to leave an empty .git directory on the host workspace root. VS Code Git then repeatedly treats the folder as a Git repository candidate and spawns git rev-parse --show-toplevel, causing the VS Code extension host to consume high CPU continuously.

Environment

  • VS Code Server: 1.124.2
  • Codex VS Code extension: openai.chatgpt 26.609.30741 linux-x64
  • Platform: Linux remote environment, likely WSL2
  • Codex sandbox: workspace-write / bubblewrap
  • Observed affected workspaces:
  • $HOME/avatra
  • $HOME/tmp/cf

Observed Behavior

  1. The workspace is not a Git repository and initially has no valid .git directory.
  2. After using Codex in the workspace, an empty host-visible .git directory appears at the workspace root.
  3. The directory has no .git/HEAD.
  4. VS Code Git repeatedly runs:

``text
git -c core.hooksPath=/dev/null -c core.fsmonitor= rev-parse --show-toplevel
``

  1. Git fails with:

``text
fatal: not a git repository (or any of the parent directories): .git
``

  1. The VS Code extension host stays at roughly 50-65% CPU.

Evidence

For one affected extension host PID, a 10 second strace sample showed:

  • 70,503 syscalls
  • 4,256 execve calls
  • 304 clone calls
  • 611 wait4 calls
  • 211 /usr/bin/git executions in $HOME/tmp/cf

The repeated command was:

git -c core.hooksPath=/dev/null -c core.fsmonitor= rev-parse --show-toplevel

The trace showed:

  • $HOME/tmp/cf/.git exists
  • $HOME/tmp/cf/.git/HEAD returns ENOENT

The same pattern was previously observed in another workspace, with 324 git rev-parse executions in 10 seconds.

A Codex sandbox command line also included a mount entry like:

--tmpfs $HOME/tmp/cf/.git --remount-ro

That suggests the sandbox may be creating a host-side mountpoint for .git, then leaving the empty directory behind after sandbox use.

Expected Behavior

Codex sandbox setup should not leave an empty .git directory on the host filesystem for a non-Git workspace. If a private .git mount is needed inside the sandbox, it should not persist as a host-visible directory after the sandbox exits.

Actual Behavior

An empty host-visible .git directory remains. VS Code Git sees it and repeatedly retries Git discovery, causing high CPU in the extension host.

Related Log Noise

The Codex log also repeatedly contains:

Received broadcast but no handler is configured method=thread-stream-state-changed

This may be separate, but strace shows the dominant CPU source is the Git discovery loop caused by the empty .git directory.

Feedback UI Issue

I also attempted to submit this via /feedback. The UI showed no confirmation and no session ID, although the Codex manual says a session ID should be shown after submission.

The local Codex log contains:

codex_app_server::request_processors::feedback_doctor_report: timed out running doctor report for feedback; skipping attachment

So I cannot tell whether the in-product feedback submission actually succeeded.

Workarounds

  • Remove the empty .git directory with rmdir <workspace>/.git
  • Disable Git integration for the affected workspace:

``json
{
"git.enabled": false
}
``

  • Reload or restart the VS Code extension host

View original on GitHub ↗

2 Comments

hkwi · 2 months ago

Related issues I found after filing:

  • #26463 looks closely related on the sandbox side: Linux sandbox creates or mounts a synthetic child .git path as tmpfs. The difference here is that I observed an empty host-visible .git directory left behind in a non-Git workspace, followed by VS Code Git repeatedly running git rev-parse --show-toplevel.
  • #17394 looks closely related on the IDE-extension CPU side: high CPU in non-Git workspaces from repeated Git metadata handling. The difference here is that strace showed the repeated process spawning path from the extension host: 211 /usr/bin/git executions in 10 seconds.
  • #19326 is related to missing Git metadata / missing HEAD, but for an unborn Git repository rather than a host-visible empty .git directory created around sandbox activity.

So this may be a cross-product of #26463 and #17394: sandbox leaves or exposes a .git path that makes the IDE Git metadata/discovery loop pathological.

nken-eccs · 1 month ago

I can confirm this issue is still present in openai.chatgpt-26.707.91948-linux-x64.

In my case, $HOME was not a Git repository, but $HOME/.git existed as a completely empty directory. While Codex was active, the Extension Host repeatedly ran:

git -c core.hooksPath=/dev/null -c core.fsmonitor= rev-parse --show-toplevel

I observed approximately 750–1,287 executions every 5 seconds.

  • Extension Host CPU: 59–66%, mostly system CPU
  • Minor faults: 30,000–46,000/s
  • Voluntary context switches: 570–814/s
  • codex app-server CPU: only about 2–4%

Confirmed loop

GitInitWatcher.start()
  -> checkForGitDirectory("$HOME/.git")
  -> stat(".git") succeeds
  -> watcher is disposed and onGitInit() is called
  -> handleGitInit()
  -> invalidateStableMetadata()
  -> getStableMetadata()
  -> git rev-parse --show-toplevel
  -> exit 128 / null metadata
  -> ensureWatchingForGitInit()
  -> new GitInitWatcher.start()
  -> stat(".git") succeeds again

Because the empty .git directory continues to exist, the newly created watcher immediately follows the same path. There is no delay or backoff between attempts.

Renaming the empty directory stopped the loop without reloading VS Code or restarting the Extension Host:

mv "$HOME/.git" "$HOME/.git.empty"

In the next measurement window:

  • repeated rev-parse executions dropped to 0
  • Extension Host CPU dropped to 0.2–0.4%
  • minor faults dropped to 23–88/s
  • voluntary context switches dropped to 13–17/s

Suggested fixes

  1. checkForGitDirectory() should not treat a successful stat(".git") as completed Git initialization. It should verify that the path resolves to valid Git metadata before disposing the watcher and calling onGitInit().
  1. When handleGitInit() receives null metadata, it should not call ensureWatchingForGitInit() immediately. The retry should use a bounded exponential backoff, for example:

``text
1s -> 2s -> 5s -> 10s -> 30s maximum
``

  1. A negative Git-root result should be cached per host and cwd for the backoff period, so repeated metadata invalidation cannot immediately spawn another git process.
  1. If the sandbox creates .git as a host-side mountpoint, it should remove that directory when the sandbox exits if it did not exist before and is still empty.
  1. Add a regression test that creates an empty .git directory in a non-Git cwd and verifies that Git discovery attempts remain bounded.