Git Directory Stays Protected as a gitdir File
The mistake
Assuming that because your working directory doesn't contain a literal .git folder — it's a git worktree, or a submodule, where .git is actually a one-line pointer file naming the real git directory somewhere else — the recursive protection on .git simply doesn't apply, and Codex should be free to write there under workspace-write.
Why this happens
The protection is specified against .git as a path, and a pointer file at that path resolves to a real directory elsewhere on disk the same way it would for git itself. The sandbox follows that resolution rather than stopping at the pointer file's literal bytes, so the protected root ends up being the resolved git directory, not just whatever sits at the .git path in the worktree you're standing in. If you've only ever worked in a plain, non-worktree checkout, there's no reason you'd have noticed this distinction exists at all.
Why it matters
Worktrees and submodules are exactly the setup where someone might reasonably expect to script around git internals directly — moving refs, editing hooks, touching objects — because the .git folder "isn't really there" in the directory Codex is running in. It still is, functionally, and the protection follows it. Assuming otherwise leads to confusing denied-write errors on a path that doesn't even look like .git in a plain directory listing.
The fix
Don't route around the protection by chasing the resolved path — the boundary is deliberate, recursive, and specifically written to survive exactly this kind of indirection. If Codex needs to perform an actual git operation, invoke git itself as a shell command (git commit, git worktree add, and so on); that's the supported path, subject to whatever approval mode you're running under, and it doesn't touch the protected internals directly at all.
This is worth internalizing specifically if your team standardizes on worktrees for running several Codex sessions in parallel against the same repository — a workflow this site covers separately, and one where every single worktree's .git pointer resolves back to the same protected root, not a separate one per worktree. That's a feature, not a limitation: it means the protection can't be quietly bypassed just by adding another worktree and hoping the new one behaves differently.
See also
The protected-paths reference covers every root this protection applies to, .agents and .codex alongside .git, and what "recursive" actually means for nested directories.
Verified 2026-08-09 against CodexHow facts module (src/data/facts/) — see /about/#accuracy.