Skip to content

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

Open

Description

@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:

    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"
  2. Launch Codex from inside that repo with workspace-write and network enabled.

  3. Confirm initial state inside Codex:

    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:

    Local HEAD:         15b583740b743ac3fe454b0934ec7d07775509ef
    Remote branch hash: 1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9
    Local tracking ref: 1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9
    
    ## codex-git-sandbox-repro-branch...origin/codex-git-sandbox-repro-branch [ahead 1]
    
  4. Run exactly one push attempt inside Codex:

    branch="$(git branch --show-current)"
    git push origin "HEAD:refs/heads/$branch"
    echo "PUSH_EXIT=$?"
  5. Observed push output:

    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
    
  6. Verify remote and local tracking state:

    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:

    HEAD=15b583740b743ac3fe454b0934ec7d07775509ef
    REMOTE=15b583740b743ac3fe454b0934ec7d07775509ef
    TRACKING=1c583d8e72b11e0077a9c59b237b1d2d67fdb7f9
    
    ## codex-git-sandbox-repro-branch...origin/codex-git-sandbox-repro-branch [ahead 1]
    
  7. Try one repair fetch:

    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:

    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

Activity

  1. added
    bugSomething isn't working
    on May 9, 2026
  2. added
    CLIIssues related to the Codex CLI
    sandboxIssues related to permissions or sandboxing
    on May 9, 2026
  3. github-actions commented on May 9, 2026

    @github-actions
    Contributor

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

  4. alvbd commented on May 9, 2026

    @alvbd
    Author

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

    I reviewed the bot-suggested possible duplicate #21304. I do not think this issue is a duplicate.

    #21304 is Windows/Desktop-focused and concerns .git/FETCH_HEAD: Permission denied caused by the Windows sandbox / orphan-SID Deny ACE behavior. It was also closed after the reporter corrected the framing around the Desktop Full Access toggle.

    This issue is different in two important ways:

    1. Platform/surface: this repro is Codex CLI on WSL2/Linux using sandbox_mode = "workspace-write" with [sandbox_workspace_write].network_access = true, not Codex Desktop on native Windows.
    2. Failure mode: this is not just “Git cannot write .git/FETCH_HEAD”. The git push updates the remote branch first, then fails updating the local remote-tracking ref under .git/refs/remotes/origin/*, while still exiting 0.

    That creates a push-specific half-success state:

    • remote branch equals local HEAD
    • local origin/<branch> remains stale
    • git status --short --branch still reports [ahead 1]
    • a blind retry or ordinary “push failed” diagnosis is misleading

    So #21304 is related background for the broader protected-gitdir/sandbox problem, but this issue is specifically about git push allowing an irreversible remote side effect before the protected local gitdir write fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't workingsandboxIssues related to permissions or sandboxing

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions