Repository navigation
[Bug][Windows] Test runner leaks thousands of TEMP directories and can trigger ProfSvc logon delays #4762
Description
Client or integration
Other — repository test runner
Area
Platform (Windows / macOS / Linux)
Summary
On Windows, OpenCodex tests create opencodex-test-* and ocx-* directories directly beneath the user's TEMP directory. A completely successful test run can leave an opencodex-test-* tree behind, while numerous fixtures either do not clean their ocx-* directories or suppress exhausted cleanup failures.
Repeated test runs accumulated at least 9,272 attributable OpenCodex top-level directories on one workstation. Windows ProfSvc then spent approximately 15–21 seconds processing the user profile during logon. After the accumulated test TEMP tree was quarantined, logon returned to approximately 3 seconds.
The test isolation environment sets HOME, USERPROFILE, OPENCODEX_HOME, and CODEX_HOME, but does not redirect TEMP, TMP, or TMPDIR into the owned test root. Expected behavior is that all test-created temporary files are contained beneath one owned root and removed after successful, failed, and interrupted runs, with ownership-safe recovery of stale roots.
Reproduction
-
On Windows, create a disposable scratch directory outside the normal user TEMP directory.
-
In the reproduction shell only, set
TEMPandTMPto that scratch directory. -
Record the scratch directory contents.
-
Run:
bun run test tests/lib/strict-semver.test.ts
-
Confirm that all tests pass.
-
Inspect the scratch directory after Bun exits.
Observed with Bun 1.4.2: the test completes with 7 passed and 0 failed, but one opencodex-test-* directory remains.
Repeated full-suite runs additionally create many sibling ocx-* directories. Forced termination, timeouts, open handles, antivirus/indexer locks, and other transient Windows file locks increase the residue, but they are not required to reproduce the bug.
Source observations:
scripts/test.tscreates anopencodex-test-*wrapper root and sets HOME-related variables, but not TEMP/TMP/TMPDIR.tests/preload.tscreates another isolation root and relies on best-effortprocess.on("exit")removal.tests/helpers/isolated-codex-home.tscreates directocx-*roots and intentionally suppresses cleanup errors after retries.- v2.51.0 contains 870 direct
mkdtempSync(join(tmpdir(), ...))calls across 465 files. Currentdevcontains 935 across 515 files.
The minimal containment fix appears to be:
- Create a temp subtree inside the owned test-run root.
- Set TEMP, TMP, and TMPDIR to that subtree before spawning test children.
- Have the preload inherit it so direct
os.tmpdir()fixtures become descendants of the owned root. - Add a strict, versioned ownership marker.
- Remove the complete owned root with bounded retries and visible deferred-cleanup reporting.
- Reclaim only stale, marker-owned roots on a later invocation.
A Bun 1.4.2 probe confirmed that changing TEMP/TMP after importing tmpdir changes the returned directory, so cached temp-path behavior does not block containment.
Version
Reproduced on v2.51.0 (c155cc7923dbc0102e27d79185505a85d4357b2c). The relevant scripts/test.ts behavior is unchanged on v2.56.0 and current dev; current dev has more direct TEMP producers.
Historical introduction appears to span:
- v2.7.35: outer
opencodex-test-*root - v2.8.0: preload-created second root
- v2.13.0: retry cleanup helper
- v2.24.2: exhausted cleanup is deliberately suppressed
Operating system
Windows 11, NTFS user profile, Bun 1.4.2
Provider and model
Not provider- or model-specific.
Logs or error output
Focused reproduction:
7 pass
0 fail
1 opencodex-test-* tree remains after process exit
Two Windows user-profile ETL captures each contained:
7,684 matching ActivityError events
7,424 x 0x80070005
260 x 0x800701C0
Relevant trace context:
profsvc.dll
CUserProfile_Load
SetReserveAreaOnFileTree
onecore\drivers\storage\storsvc\storagereserve\storagereserveinternal.cppThe ETLs establish recursive profile processing during the delay but do not independently name individual TEMP child directories. Attribution rests on the combined evidence: exact OpenCodex producer names, matching accumulated directory families, a green-run reproduction, repeated-run counts, and the logon timing improvement after quarantine.
This is the same class of Windows impact reported in OpenCode #27005 and in a Microsoft support case where an oversized Local Temp directory delayed User Profile Service logon. Neither is an OpenCodex duplicate.
Screenshots and supporting files
Available privately if maintainers need them:
- Pre- and post-reserved-storage ETLs
- Decoded event summaries
- Quarantine prefix inventory
- Safe redirected-TEMP reproduction results
No existing OpenCodex issue was found for this defect. Issue #499 and PR #502 concern a different abandoned temporary-file class, but provide a useful ownership-safe recovery precedent.
Redacted configuration
{
"providerSpecific": false,
"providerConfigurationRequired": false
}Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
assisted by Sol/high
Activity
lidge-jun commentedon Sep 16, 2026
리뷰 · 우선순위 71 / 80
이 이슈는 Windows에서 테스트 러너가 TEMP 아래에
opencodex-test-*/ocx-*디렉터리를 잔뜩 남기고, 그게 쌓이면 ProfSvc 로그온이 느려진다는 실측 보고다. 현재devHEADb3035fe292(패키지 2.57.0)의scripts/test.ts를 보면createIsolatedTestEnvironment(약 19–70행)가mkdtempSync(join(tmpdir(), "opencodex-test-"))로 루트를 만들고 HOME/USERPROFILE/OPENCODEX_HOME/CODEX_HOME만 그 안으로 옮긴다. TEMP/TMP/TMPDIR는 건드리지 않는다.tests/preload.ts도 별도 isolation 루트를 만들고process.on("exit")(약 118–121행)에서 best-effortrmSync만 한다.tests/helpers/isolated-codex-home.ts(약 11–35행)는ocx-codex-home-접두로tmpdir()에 직접 만들고, Windows 핸들 레이스를 이유로 cleanup 실패를 삼킨다. 주석이 ‘stale directory under TEMP costs nothing’이라고 쓰여 있는데, 이 이슈의 ETL·로그온 실측은 그게 Windows에서는 비용이 된다는 반례다.재현 절차도 구체적이다. TEMP/TMP만 scratch로 돌린 뒤
bun run test tests/lib/strict-semver.test.ts가 전부 통과해도opencodex-test-*하나가 남는다는 것이다. 전체 스위트를 반복하면 수천 개가 쌓이고, 보고자는 9,272개·로그온 15–21초 → 격리 후 약 3초를 적었다. OpenCode #27005 / Microsoft Q&A와 같은 계급이지 OpenCodex 중복은 아니다. #499/#502는 다른 임시파일 계급이지만 ownership-safe 회수 선례로는 맞다.고침 방향도 이미 이슈에 정리돼 있다. owned test-run root 안에 temp 서브트리를 만들고 TEMP/TMP/TMPDIR를 거기로 돌린 뒤, preload·직접
os.tmpdir()픽스처가 전부 그 자손이 되게 하고, 버전 있는 ownership 마커 + 재시도 삭제 + stale 회수만 허용하면 된다. Bun 1.4.2에서 TEMP를 나중에 바꿔도tmpdir()가 따라간다는 프로브도 있어 캐시된 temp 경로 걱정은 작다.제품 런타임 버그는 아니고 Windows 기여자·CI/로컬 DX 버그다. 그래도 ProfSvc까지 가는 실측이 있고 tip에도 그대로라 무시하면 안 된다. types/config 스플릿과 무관하고 미리보기 배포도 없다.
경로
scripts/test.tscreateIsolatedTestEnvironment(22, 39–70) - HOME계만 샌드박스하고 TEMP/TMP/TMPDIR는 그대로다.경로
tests/preload.ts(118–121) - exit 훅 best-effort 삭제. 성공·실패·중단을 엄격히 보장하지 않는다.경로
tests/helpers/isolated-codex-home.ts(11–33) -ocx-*를 tmpdir에 직접 만들고 cleanup 실패를 삼킨다.심볼 직접 mkdtempSync(join(tmpdir())) - tip에 픽스처가 많고, Windows에서 residue가 로그온 비용이 된다.
메인테이너의 판단이 필요한 지점
scripts/test.ts래퍼만 할지, preload·헬퍼까지 강제할지.너의 추천
받아라. Windows platform 레인으로 containment PR을 받되, 먼저
scripts/test.ts에서 TEMP/TMP/TMPDIR를 owned root 하위로 돌리고 residue 회귀 테스트를 붙여라. cleanup 삼킴 주석은 Windows 비용 문장으로 고쳐라.이 댓글은 grok-bot이 작성했습니다
fix(test): contain and reclaim Windows temporary roots (#4785, #4789) (…
lidge-jun commentedon Sep 16, 2026
Landed via #4796 at dc9d1fa
fix(test): contain and reclaim Windows temporary roots (lidge-jun#4785,