workspace-write + network_access=true lets git push update remote before failing local tracking-ref update

Open 💬 2 comments Opened May 9, 2026 by alvbd

What version of Codex CLI is running?

codex-cli 0.129.0

What subscription do you have?

ChatGPT Pro

Which model were you using?

gpt-5.5 This does not appear model-specific; the failure occurs in the shell/Git command path.

What platform is your computer?

Linux 6.6.87.2-microsoft-standard-WSL2 x86_64 GNU/Linux, WSL distro: Ubuntu-26.04, Git: git version 2.53.0, GitHub CLI: gh version 2.92.0 (2026-04-28)

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

Windows Terminal launching WSL2 Ubuntu-26.04

What issue are you seeing?

In Codex CLI sessions using sandbox_mode = "workspace-write" with [sandbox_workspace_write].network_access = true, git push can update the remote branch successfully, then fail when Git tries to update the local remote-tracking ref under .git/refs/remotes/origin/*.

In my repro, the push command printed a successful remote update and then a local .git/refs/remotes/origin/<branch>.lock failure caused by Read-only file system. The command still exited 0.

This leaves the repository in a misleading half-success state:

  • remote branch equals local HEAD
  • local origin/<branch> remains stale
  • git status --short --branch still reports [ahead 1]
  • git fetch origin <branch> also fails because .git/FETCH_HEAD is read-only

This is more hazardous than ordinary .git/index.lock failures. With git push, the remote side effect can already have happened before the local .git metadata write fails. An agent that treats this as an ordinary failed push may waste time debugging SSH, GitHub auth, branch protection, ownership, or repository corruption, or may retry blindly even though the remote already moved.

The important distinction is:

The problem is not merely that Git cannot write .git; the problem is that Codex may allow the irreversible remote side effect before the protected local .git write fails.

Relevant sandbox config:

approval_policy = "on-request"
sandbox_mode = "workspace-write"

[sandbox_workspace_write]
network_access = true

Relevant environment variables captured inside the Codex session, redacted:

CODEX_CI=1
CODEX_HOME=/home/<user>/.codex
CODEX_MANAGED_BY_NPM=1
CODEX_THREAD_ID=<redacted>
PWD=/home/<user>/code/codex-git-sandbox-repro-20260509-103731
WSL2_GUI_APPS_ENABLED=1
WSLENV=WT_SESSION:WT_PROFILE_ID:
WSL_DISTRO_NAME=Ubuntu-26.04
WSL_INTEROP=/run/WSL/<redacted>_interop

What steps can reproduce the bug?

I reproduced this in a disposable private GitHub repo created only for this test.

Initial setup outside Codex:

  1. Create a fresh repo under a normal WSL Linux path, not /tmp:

```bash
stamp="$(date +%Y%m%d-%H%M%S)"
repo="codex-git-sandbox-repro-$stamp"
branch="codex-git-sandbox-repro-branch"
root="$HOME/code/$repo"

mkdir -p "$root"
cd "$root"

git init -b "$branch"
git config user.name "Codex Sandbox Repro"
git config user.email "codex-git-sandbox-repro@example.invalid"

printf 'baseline\n' > README.md
git add README.md
git commit -m "baseline"

gh repo create "<account>/$repo" --private --source=. --remote=origin --push

printf 'local ahead commit\n' >> README.md
git add README.md
git commit -m "local ahead commit"
```

  1. Launch Codex from inside that repo with workspace-write and network enabled.
  1. Confirm initial state inside Codex:

``bash
git status --short --branch
git branch --show-current
git rev-parse HEAD
git rev-parse "refs/remotes/origin/$(git branch --show-current)" || true
git ls-remote --heads origin "$(git branch --show-current)"
``

Initial state from my repro:

```text
Local HEAD: 15b583740b743ac3fe454b0934ec7d07775509ef
Remote branch hash: 1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9
Local tracking ref: 1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9

## codex-git-sandbox-repro-branch...origin/codex-git-sandbox-repro-branch [ahead 1]
```

  1. Run exactly one push attempt inside Codex:

``bash
branch="$(git branch --show-current)"
git push origin "HEAD:refs/heads/$branch"
echo "PUSH_EXIT=$?"
``

  1. Observed push output:

``text
To github.com:<account>/codex-git-sandbox-repro-20260509-103731.git
1c583d8..15b5837 HEAD -> codex-git-sandbox-repro-branch
error: update_ref failed for ref 'refs/remotes/origin/codex-git-sandbox-repro-branch': cannot lock ref 'refs/remotes/origin/codex-git-sandbox-repro-branch': Unable to create '/home/<user>/code/codex-git-sandbox-repro-20260509-103731/.git/refs/remotes/origin/codex-git-sandbox-repro-branch.lock': Read-only file system
PUSH_EXIT=0
``

  1. Verify remote and local tracking state:

```bash
branch="$(git branch --show-current)"
head="$(git rev-parse HEAD)"
remote="$(git ls-remote --heads origin "$branch" | awk '{print $1}')"
tracking="$(git rev-parse "refs/remotes/origin/$branch" 2>/dev/null || true)"

printf 'HEAD=%s\n' "$head"
printf 'REMOTE=%s\n' "$remote"
printf 'TRACKING=%s\n' "$tracking"
git status --short --branch
```

Post-push state from my repro:

```text
HEAD=15b583740b743ac3fe454b0934ec7d07775509ef
REMOTE=15b583740b743ac3fe454b0934ec7d07775509ef
TRACKING=1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9

## codex-git-sandbox-repro-branch...origin/codex-git-sandbox-repro-branch [ahead 1]
```

  1. Try one repair fetch:

``bash
git fetch origin "$(git branch --show-current)"
echo "FETCH_EXIT=$?"
git rev-parse "refs/remotes/origin/$(git branch --show-current)" || true
git status --short --branch
``

Repair output:

``text
error: cannot open '.git/FETCH_HEAD': Read-only file system
FETCH_EXIT=255
1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9
## codex-git-sandbox-repro-branch...origin/codex-git-sandbox-repro-branch [ahead 1]
``

What is the expected behavior?

Codex should avoid or correctly handle the half-success git push state.

Acceptable behavior would be any of:

  1. In workspace-write, allow normal Git metadata writes for the current worktree gitdir when the worktree itself is writable.
  2. If .git is intentionally protected, detect Git commands that require gitdir writes and require escalation before running them.
  3. Special-case git push so remote side effects cannot happen before Codex has a writable path for local tracking metadata.
  4. At minimum, when a push reports a local remote-tracking ref lock/update failure, Codex should verify the remote branch with git ls-remote before diagnosing the operation as a failed push.

Expected agent behavior after this error:

branch="$(git branch --show-current)"
head="$(git rev-parse HEAD)"
remote="$(git ls-remote --heads origin "$branch" | awk '{print $1}')"

if [ "$head" = "$remote" ]; then
  echo "Remote matches HEAD; push succeeded remotely. Local tracking ref may be stale."
else
  echo "Remote does not match HEAD; push did not complete as expected."
fi

If remote equals local HEAD, Codex should not retry the push blindly and should not investigate SSH credentials, GitHub auth, branch protection, filesystem ownership, or repository corruption unless there is separate evidence for those problems.

Additional information

Result classification: REPRODUCED

The remote branch matched local HEAD, but the local remote-tracking ref stayed stale and Git reported a .git/refs/remotes/origin/*.lock update failure after the remote branch update.

Summary of hashes:

Before push:
HEAD     = 15b583740b743ac3fe454b0934ec7d07775509ef
REMOTE   = 1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9
TRACKING = 1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9

After push:
HEAD     = 15b583740b743ac3fe454b0934ec7d07775509ef
REMOTE   = 15b583740b743ac3fe454b0934ec7d07775509ef
TRACKING = 1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9

This appears related to, but sharper than, the general writable-gitdir problem because git push can perform the remote side effect before the local tracking-ref write fails.

Related issues:

  • openai/codex#14338 — allow writable gitdir for current worktree in sandboxed workspace-write mode
  • openai/codex#5034 — unable to use Git even with workspace-write / .git/index.lock
  • openai/codex#15310 — sandbox policy inconsistency and .git/FETCH_HEAD / ref lockfile failures
  • openai/codex#15292 — allowed Git commands still requiring sandbox escape for .git/index.lock

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗