Symlinks in `permissions.*.filesystem` dropped instead of passed-through to bwrap with real path

Open 💬 1 comment Opened Jul 21, 2026 by aowenson-imm

What version of Codex CLI is running?

0.144.5

What subscription do you have?

ChatGPT Edu

Which model were you using?

gpt-5.6-terra

What platform is your computer?

Linux 6.8.0-107-generic x86_64 x86_64

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

Bash

What issue are you seeing?

If a path in my [permissions.*.filesystem] is a symlink, then after Codex canonicalizes it to pass real path to bwrap, it discard the symlink. So anything inside Codex session using the symlink instead of real path, breaks. Even if I have both the symlink and real path in config.

Baseline: this works:

$ cat ~/.codex/config.toml
approval_policy = "on-request"
default_permissions = "codex_sandbox"
[permissions.codex_sandbox.filesystem]
:minimal "read"

But if I manually enumerate the minimal paths:

$ cat ~/.codex/config.toml
approval_policy = "on-request"
default_permissions = "codex_sandbox"
[permissions.codex_sandbox.filesystem]
"/bin" = "read"
"/sbin" = "read"
"/lib" = "read"
"/lib64" = "read"
"/etc" = "read"
"/usr" = "read"

Then Codex gets this error when trying to run anything:

Failed to execvp /bin/sh: No such file or directory (os error 2)

A separate analysis with working Codex thinks problem is:

For literal rules, Codex canonicalizes the path before constructing the Bubblewrap mounts. On Ubuntu 22:

/bin    -> /usr/bin
/sbin   -> /usr/sbin
/lib    -> /usr/lib
/lib64  -> /usr/lib64

So then /bin is dropped.

What steps can reproduce the bug?

Set your config to:

$ cat ~/.codex/config.toml
approval_policy = "on-request"
default_permissions = "codex_sandbox"
[permissions.codex_sandbox.filesystem]
"/bin" = "read"
"/sbin" = "read"
"/lib" = "read"
"/lib64" = "read"
"/etc" = "read"
"/usr" = "read"

Then ask Codex to run any Linux command.

What is the expected behavior?

Don't drop paths that were canonicalized, match what :minimal does:

:minimal takes a different code path: Codex injects /bin, /sbin, /usr, /etc, /lib, and /lib64 as raw Bubblewrap mount targets, preserving /bin in the namespace. That is why it works.

View original on GitHub ↗

1 Comment

rinsuki · 10 days ago

I have same problem, but on macOS.

I have a dotfiles repository at ~/dotfiles and creating symlink to ~/ from ~/dotfiles/home/.

$ ls -l ~/.gitconfig
lrwxr-xr-x@ 1 user  staff  36  7月  6 18:27 /Users/user/.gitconfig -> /Users/user/dotfiles/home/.gitconfig

and i have a config like:

[permissions.editor_base.filesystem]
":minimal" = "read"
":tmpdir" = "read"

"~/dotfiles" = "read"
"~/.gitconfig" = "read"

but symlinks will be resolved to actual path when creating sandbox:

        "-DREADABLE_ROOT_2=/Users/user/dotfiles/home/.gitconfig",
        "-DREADABLE_ROOT_3=/Users/user/dotfiles",

so cat ~/.gitconfig fails because it can't read symlink at ~/.gitconfig:

Sandbox: cat(34020) deny(1) file-read-metadata /Users/user/.gitconfig