Test binaries leak temporary CODEX_HOME directories after exit

Open 💬 0 comments Opened Jul 11, 2026 by askac

What issue are you seeing?

Codex integration test binaries leave temporary CODEX_HOME directories behind after a normal test process exit.

The leaked directories are named codex-core-tests* and codex-exec-server-tests*. They contain nested arg0 helper directories, lock files, and symlinks to the test binary.

This reproduces on both FreeBSD and WSL2/Linux. On my FreeBSD development system, repeated test runs accumulated 879 codex-core-tests* directories under /tmp.

What steps can reproduce the bug?

Using commit 5c19155cbd93bfa099016e7487259f61669823ff:

  1. Create an isolated temporary root:

probe_root=$(mktemp -d /tmp/codex-static-leak.XXXXXX)

  1. From codex-rs, run one core integration test:

TMPDIR="$probe_root" just test \
-p codex-core \
--test all \
-E 'test(=suite::resume_warning::emits_warning_when_resumed_model_differs)'

  1. Inspect the temporary root:

find "$probe_root" -maxdepth 5 -printf '%y %p -> %l\n' | sort

After the test exits successfully, multiple codex-core-tests* directories remain. In one WSL2 run, a single filtered test left three directories.

The same lifetime issue affects the exec-server integration test guard.

What is the expected behavior?

Temporary CODEX_HOME directories created by integration test binaries should be removed after a normal process exit.

Forced termination such as SIGKILL does not need to be handled, because process destructors cannot run in that case.

Additional information

Root cause:

codex-rs/core/tests/suite/mod.rs and codex-rs/exec-server/tests/common/mod.rs store TestBinaryDispatchGuard in #[ctor] statics. Rust statics are not dropped at process exit, so the owned tempfile::TempDir values never perform cleanup.

I prepared and tested a candidate fix:

https://github.com/askac/codex/commit/69b91a030206c5a36936d294130e9b8894f8ada8

Full comparison:

https://github.com/openai/codex/compare/main...askac:fix-test-binary-tempdir-cleanup

The candidate fix:

  • stores each guard in a regular static Mutex<Option<_>>;
  • takes and drops the guard from a destructor on normal exit;
  • avoids holding the mutex while the exec-server test mode is running;
  • drops the nested arg0 guard before its parent temporary CODEX_HOME, which is important on Windows while the lock file is open;
  • adds a self-reexec regression test using an isolated temporary root.

Verification:

  • WSL2/Linux core regression test: passed;
  • FreeBSD exec-server relay integration test: passed;
  • no new codex-core-tests* or codex-exec-server-tests* directories remained after the successful patched test runs.

The repository currently limits pull request creation to collaborators, so I am providing the analysis, reproduction, and public candidate fix here first.

View original on GitHub ↗