Skip to content

[Bug][Windows] Test runner leaks thousands of TEMP directories and can trigger ProfSvc logon delays #4762

Closed

Description

@colthreepv

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

  1. On Windows, create a disposable scratch directory outside the normal user TEMP directory.

  2. In the reproduction shell only, set TEMP and TMP to that scratch directory.

  3. Record the scratch directory contents.

  4. Run:

    bun run test tests/lib/strict-semver.test.ts
  5. Confirm that all tests pass.

  6. 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.ts creates an opencodex-test-* wrapper root and sets HOME-related variables, but not TEMP/TMP/TMPDIR.
  • tests/preload.ts creates another isolation root and relies on best-effort process.on("exit") removal.
  • tests/helpers/isolated-codex-home.ts creates direct ocx-* roots and intentionally suppresses cleanup errors after retries.
  • v2.51.0 contains 870 direct mkdtempSync(join(tmpdir(), ...)) calls across 465 files. Current dev contains 935 across 515 files.

The minimal containment fix appears to be:

  1. Create a temp subtree inside the owned test-run root.
  2. Set TEMP, TMP, and TMPDIR to that subtree before spawning test children.
  3. Have the preload inherit it so direct os.tmpdir() fixtures become descendants of the owned root.
  4. Add a strict, versioned ownership marker.
  5. Remove the complete owned root with bounded retries and visible deferred-cleanup reporting.
  6. 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.cpp

The 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

  1. added
    bugSomething isn't working
    platformOS/service/tray/ACL (Windows-heavy, not Windows-only)
    on Sep 16, 2026
  2. lidge-jun commented on Sep 16, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 71 / 80

    이 이슈는 Windows에서 테스트 러너가 TEMP 아래에 opencodex-test-* / ocx-* 디렉터리를 잔뜩 남기고, 그게 쌓이면 ProfSvc 로그온이 느려진다는 실측 보고다. 현재 dev HEAD b3035fe292(패키지 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-effort rmSync만 한다. 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.ts createIsolatedTestEnvironment (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가 로그온 비용이 된다.

    메인테이너의 판단이 필요한 지점

    너의 추천
    받아라. Windows platform 레인으로 containment PR을 받되, 먼저 scripts/test.ts에서 TEMP/TMP/TMPDIR를 owned root 하위로 돌리고 residue 회귀 테스트를 붙여라. cleanup 삼킴 주석은 Windows 비용 문장으로 고쳐라.

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Sep 16, 2026

    @lidge-jun
    Owner

    Landed via #4796 at dc9d1fa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinglanded-via-maintainerOriginal PR closed after landing via a maintainer merge trainplatformOS/service/tray/ACL (Windows-heavy, not Windows-only)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions