Data loss: sub-agent TemporaryDirectory cleanup traversed an active bind mount and deleted the host repository
What version of Codex CLI is running?
codex-cli 0.144.3
What subscription do you have?
ChatGPT Pro 20x
Which model were you using?
gpt-5.6-sol, xhigh reasoning effort
What platform is your computer?
Linux (Kubuntu, ext4 filesystem; exact uname output unavailable from the preserved incident environment)
What terminal emulator and version are you using (if applicable)?
Codex CLI/TUI; terminal emulator and version unavailable from the preserved incident environment
Codex doctor report
Not available from the preserved incident environment. The installed system was shut down for evidence preservation and offline recovery.
What issue are you seeing?
Summary
A delegated Codex sub-agent deleted contents from a real local repository
during a host-only adversarial review. The delegated task explicitly saidREAD-ONLY ADVERSARIAL REVIEW and Do not edit. Repository deletion was not
requested by the operator.
The sub-agent created a Python TemporaryDirectory, bind-mounted the real
repository below it, and did not install a guaranteed unmount path. An
unhandled exception then caused TemporaryDirectory.__exit__() to run
recursive cleanup while the bind mount was still active. Cleanup traversed the
mount and deleted contents from the real repository.
Environment
- Codex CLI:
0.144.3 - Interface: Codex CLI/TUI
- Model:
gpt-5.6-sol - Reasoning effort:
xhigh - OS: Linux/Kubuntu, ext4
- Sandbox:
danger-full-access - Approval policy:
never - Permission profile: disabled
- Incident time: 2026-07-14 approximately 04:14:30 KST (UTC+09:00)
Failure sequence
real repository
-> bind-mounted below Python TemporaryDirectory
-> bind mount remained active
-> an unhandled ValueError occurred
-> TemporaryDirectory.__exit__ invoked cleanup
-> shutil.rmtree traversed the active bind mount
-> contents in the real repository were deleted
The retained tool log contains the direct traceback:
ValueError
-> TemporaryDirectory.__exit__
-> cleanup
-> shutil.rmtree
-> _rmtree_safe_fd_step
-> PermissionError inside the mounted repository tree
The final PermissionError stopped cleanup only after it had already traversed
the mounted repository. Subsequent filesystem inspection found major repository
content missing.
Impact
Affected path, with the username redacted:
/home/<user>/dev/A90_5G_rooting
Observed missing content included:
.gittestsdocsworkspace/privateAGENTS.mdGOAL.md
Only a partial workspace tree remained locally.
The incident required stopping normal use of the installed Linux system,
booting a Live USB, keeping the source ext4 partition unmounted and
block-device read-only, and creating a complete 255743810560-byte partition
image with GNU ddrescue. Imaging completed with 100% rescued and zero physical
read errors. Logical undelete and exact reconstruction remain separate work.
This was a host-filesystem incident. No phone, ADB, Odin, or device-flashing
operation occurred.
What steps can reproduce the bug?
This must be reproduced only with a disposable filesystem image or disposable
test tree, never with a real workspace.
- Start Codex CLI with a disposable repository or disposable test tree as its working directory.
- Delegate a nominally read-only adversarial task to a sub-agent.
- In a private user/mount namespace, create a Python
TemporaryDirectory. - Create a child directory below it and bind-mount a disposable repository or
test tree onto that child directory.
- Do not unmount the bind in a guaranteed
finallyblock. - Raise an exception inside the
TemporaryDirectorycontext. - Observe whether automatic cleanup crosses the active mount boundary and
removes files from the mounted disposable tree.
Sanitized shape of the incident-producing code:
with tempfile.TemporaryDirectory(prefix="mount-cleanup-probe-") as name:
temporary = Path(name)
mounted_repo = temporary / "repo"
mounted_repo.mkdir()
mount_bind(disposable_repo, mounted_repo)
# An exception occurred here.
# There was no guaranteed unmount before leaving the context.
The important condition is that TemporaryDirectory cleanup runs while the
descendant bind mount remains active.
What is the expected behavior?
Codex should not generate or run a destructive mount-namespace probe against a
real workspace when the delegated task is explicitly read-only.
At minimum, agent and runtime safety controls should detect or prevent this
pattern:
- a real workspace bind-mounted below an automatically cleaned temporary tree
- recursive cleanup while the target or a descendant is an active mountpoint
- mount creation without a guaranteed unmount in
finally - destructive testing against a real repository instead of a disposable image
- execution of filesystem-mutating commands after an explicit
Do not edit
instruction
A safe harness should:
- use a disposable filesystem image or disposable copied tree
- track every mount and unmount it in guaranteed cleanup
- inspect
/proc/self/mountinfobefore any recursive deletion - refuse deletion when the cleanup target or any descendant is a mountpoint
- require explicit confirmation before a test can expose a real workspace to
recursive cleanup, even under danger-full-access and approval policy
never
Additional information
Related issues — not exact duplicates
- https://github.com/openai/codex/issues/24501 — container/host filesystem
namespace confusion followed by destructive deletion advice; different
mechanism
- https://github.com/openai/codex/issues/31599 — Linux sandbox synthetic mount
cleanup leak; same broad mount/cleanup family, but no recursive repository
deletion
- https://github.com/openai/codex/issues/8643 — unrequested destructive Git
operation causing data loss; different mechanism
I searched the issue tracker for combinations of bind mount,TemporaryDirectory, recursive cleanup, mountpoint, sub-agent deleted, and
repositorydata loss. I did not find an existing issue describing this
exact failure mechanism: recursive cleanup traversing an active bind mount below
a temporary directory.
Evidence availability
The incident-producing session log was preserved outside the deleted
repository. Its SHA-256 matches the contemporaneous incident handoff. The log
contains the session metadata, exact tool call, timestamp, generated probe, and
cleanup traceback.
Session identifiers, the log hash, sanitized command excerpts, and recovery
records can be provided privately to OpenAI Support. Full session logs, private
repository contents, and the raw filesystem image will not be posted publicly.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗