Sandbox writable_roots bind mounts break hardlinks across roots (uv cache to .venv)
What version of Codex CLI is running?
codex-cli 0.133.0
What subscription do you have?
Plus
Which model were you using?
gpt-5.5
What platform is your computer?
Linux 6.6.114.1-microsoft-standard-WSL2 x86_64
What terminal emulator and version are you using (if applicable)?
Windows Terminal, WSL2
Codex doctor report
Omitted/redacted because the full report includes local machine paths. Relevant parts:
{
"codexVersion": "0.133.0",
"checks": {
"sandbox.helpers": {
"status": "ok",
"details": {
"approval policy": "OnRequest",
"filesystem sandbox": "restricted",
"network sandbox": "enabled"
}
},
"terminal.env": {
"details": {
"terminal": "Windows Terminal",
"WSL_DISTRO_NAME": "present"
}
}
}
}
What issue are you seeing?
When Codex runs with workspace-write and multiple writable roots, each writable root appears to be exposed as a separate bind mount. This makes hardlinks fail across writable roots even when both roots are backed by the same ext4 device.
The concrete symptom was uv warning while running a Python tool:
warning: Failed to hardlink files; falling back to full copy. This may lead to degraded performance.
If the cache and target directories are on different filesystems, hardlinking may not be supported.
In this setup:
- uv cache root is one writable root:
<home>/.cache/uv - project/worktree is another writable root:
<repo> - virtualenv is inside the project root:
<repo>/api/.venv
Inside the Codex sandbox, df shows both paths on the same ext4 device, but findmnt shows them as separate bind mounts:
TARGET SOURCE
<home>/.cache/uv /dev/sdd[<home>/.cache/uv]
<repo> /dev/sdd[<repo>]
Attempting a normal hardlink from the uv cache root into the project root fails:
ln: failed to create hard link '<repo>/api/.venv/codex-hardlink-dest.tmp' => '<home>/.cache/uv/codex-hardlink-source.tmp': Invalid cross-device link
But hardlinking within the project writable root succeeds, including into the same .venv:
<same inode> 2 <repo>/codex-hardlink-source.tmp
<same inode> 2 <repo>/api/.venv/codex-hardlink-dest.tmp
This looks related to #8636 and #10864, but those are Cargo/Rust cases. This report is specifically about hardlinks across multiple Codex writable roots, reproduced with plain ln and surfaced by uv.
What steps can reproduce the bug?
Use a workspace-write Codex session with both of these as writable roots:
<home>/.cache/uv
<repo>
Then run:
uv cache dir
readlink --canonicalize api/.venv
df --print-type <home>/.cache/uv api/.venv
findmnt --target <home>/.cache/uv --output TARGET,SOURCE,FSTYPE,OPTIONS
findmnt --target "$PWD/api/.venv" --output TARGET,SOURCE,FSTYPE,OPTIONS
Then test cross-root hardlinking:
src=<home>/.cache/uv/codex-hardlink-source.tmp
dst="$PWD/api/.venv/codex-hardlink-dest.tmp"
rm --force "$src" "$dst"
printf 'hardlink-test\n' > "$src"
ln "$src" "$dst"
Observed:
Invalid cross-device link
Control test inside the project writable root:
src="$PWD/codex-hardlink-source.tmp"
dst="$PWD/api/.venv/codex-hardlink-dest.tmp"
rm --force "$src" "$dst"
printf 'hardlink-test\n' > "$src"
ln "$src" "$dst"
stat --format '%i %h %n' "$src" "$dst"
Observed: succeeds with the same inode and link count 2.
What is the expected behavior?
If two writable roots are backed by the same underlying filesystem, hardlinks between them should either work, or Codex should document that each writable root is isolated as a separate mount and cross-root hardlinks are expected to fail.
This matters for package managers and build tools that optimize by hardlinking from caches into project-local environments, such as uv cache to .venv.
Additional information
Workarounds:
UV_LINK_MODE=copysuppresses the uv warning and uses copies instead of hardlinks.- Moving
UV_CACHE_DIRunder the project writable root should also avoid the warning, but creates project-local cache clutter.
Related issues:
- #8636
- #10864