codex-state tests leak temporary SQLite runtime directories

Open 💬 0 comments Opened Jul 11, 2026 by askac

What issue are you seeing?

codex-state runtime tests permanently leave temporary SQLite database directories under the system temporary directory.

The directories are named codex-state-runtime-test-*. On my FreeBSD development system, repeated test runs accumulated 402 directories using approximately 109 MiB in total.

A single filtered codex-state test reproduces the leak, leaving one directory containing state_5.sqlite, logs_2.sqlite, goals_1.sqlite, and memories_1.sqlite after the test exits normally.

The same filtered test also reproduces the leak on WSL2/Linux. Depending on SQLite connection shutdown timing, the leaked directory can additionally contain -wal and -shm files.

What steps can reproduce the bug?

Using commit 5c19155cbd93bfa099016e7487259f61669823ff:

  1. Create an isolated temporary root:

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

  1. From codex-rs, run one state runtime test:

TMPDIR="$probe_root" TMP="$probe_root" TEMP="$probe_root" \
just test \
-p codex-state \
-E 'test(=runtime::external_agent_config_imports::tests::records_completion_by_import_id)'

  1. Inspect the temporary root:

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

The test passes, but one codex-state-runtime-test-* directory remains with the runtime SQLite databases.

Observed FreeBSD result:

RESULT: REPRODUCED
Leftover codex-state runtime test directories: 1

Observed WSL2/Linux result:

RESULT: REPRODUCED
Leftover codex-state runtime test directories: 1

What is the expected behavior?

Temporary SQLite runtime directories created by codex-state test fixtures should remain available for the lifetime of their test and then be removed automatically when the test finishes.

Successful test runs should not permanently accumulate codex-state-runtime-test-* directories under the system temporary directory.

Additional information

Root cause:

codex-rs/state/src/runtime/test_support.rs::unique_temp_dir() returns a unique PathBuf without retaining a cleanup guard. Some callers manually invoke remove_dir_all at the end of their tests, which shows that these directories are intended to be temporary, but other callers omit cleanup.
Manual cleanup is also skipped when a test returns early or panics.

This is separate from #32374 and #32380.

#32374 involves test-binary dispatch guards retained in Rust statics. #32380 is caused by an explicit .keep() in the chatwidget fixture. This issue originates in codex-state test support, where unique_temp_dir() returns only a PathBuf and relies on individual callers to perform cleanup.

I prepared a candidate fix:

https://github.com/askac/codex/commit/5ec756e7e07350d5795d4059948eaefec1f9f13f

Full comparison:

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

The candidate fix:

  • registers each generated path with a thread-local cleanup guard;
  • keeps the SQLite runtime directory alive for the complete test-thread lifetime;
  • removes the directory when the test thread exits normally;
  • does not change production StateRuntime code;
  • does not require changes to existing test call sites;
  • does not add dependencies or lockfile changes;
  • adds a self-reexec regression test that checks for leftovers after the child test thread exits.

The FreeBSD and WSL2/Linux results were collected using the same wrapper around the reproduction commands above. The wrapper creates an isolated temporary root, runs the single filtered test, and counts matching leftover directories after the test process exits.

FreeBSD verification:

Before the fix:

PASS codex-state runtime::external_agent_config_imports::tests::records_completion_by_import_id
RESULT: REPRODUCED
Leftover codex-state runtime test directories: 1

After the fix, using the same existing test:

PASS codex-state runtime::external_agent_config_imports::tests::records_completion_by_import_id
RESULT: NOT REPRODUCED

The complete codex-state test suite also passed: 154 tests passed, 0 failed. just fix -p codex-state completed successfully.

WSL2/Linux verification using the same existing test:

Before the fix:

RESULT: REPRODUCED
Leftover codex-state runtime test directories: 1

The leaked directory contained the four runtime SQLite databases plus WAL/SHM files for the goals, memories, and state databases.

After the fix:

RESULT: NOT REPRODUCED

The patched WSL2 probe root was empty after the successful test.

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

View original on GitHub ↗