Skip to content

Fix Windows temp-file cleanup failing on paths over MAX_PATH - #9452

Open
alexreinking wants to merge 3 commits into
mainfrom
alexreinking/fix-windows-max-path-cleanup
Open

alexreinking wants to merge 3 commits into
mainfrom
alexreinking/fix-windows-max-path-cleanup

Conversation

@alexreinking

Copy link
Copy Markdown
Member

Summary

  • `compile_multitarget()` names per-subtarget temp object files by concatenating the output prefix with the full, unelided subtarget suffix (every feature name spelled out), which can push the absolute path past the legacy Windows `MAX_PATH` limit (260 chars) once combined with a long temp directory path.
  • `_unlink()`/`RemoveDirectoryW()` then silently fail for that one file (no error checking on the unlink side), so `TemporaryFileDir`'s destructor goes on to call `RemoveDirectoryW()` on a directory that still isn't actually empty, raising `error 145` (`ERROR_DIR_NOT_EMPTY`).
  • Root-caused via a live repro: at the moment of failure, Sysinternals `handle64.exe` found no process anywhere on the system holding the file open (ruling out a lock/AV/indexer race), but the failing path was exactly 261 characters versus 245 for a sibling file that always deleted fine -- squarely the `MAX_PATH` boundary.
  • Fixed by opting both `file_unlink()` and `dir_rmdir()` out of `MAX_PATH` via the well-known `\?\` long-path prefix (which requires an absolute, backslash-separated path, so the existing forward-slash paths are converted first).

Test plan

  • Reproduced the failure reliably on Windows with `test/correctness/compile_to_multitarget.cpp` (previously failing ~90% of runs)
  • Confirmed 15/15 clean runs after the fix
  • Re-ran `correctness_struct_type`, `correctness_parallel`, `correctness_async`, `correctness_make_struct`, `correctness_thread_safety` to confirm no regressions from the path-handling change

compile_multitarget() names per-subtarget temp object files by
concatenating the output prefix with the full, unelided subtarget
suffix (every feature name), which can push the absolute path past the
legacy Windows MAX_PATH limit (260 chars) once combined with a long
temp directory path. _unlink()/RemoveDirectoryW() then silently fail
for that one file, and TemporaryFileDir's destructor -- which doesn't
check file_unlink()'s return value -- goes on to call RemoveDirectoryW()
on a directory that still isn't actually empty, raising "error 145"
(ERROR_DIR_NOT_EMPTY).

Root-caused via a live repro: at the moment of failure, Sysinternals
handle64.exe found no process anywhere on the system holding the file
open (ruling out a lock/AV/indexer race), but the failing path was
exactly 261 characters versus 245 for a sibling file that always
deleted fine -- squarely the MAX_PATH boundary.

Fixed by opting both file_unlink() and dir_rmdir() out of MAX_PATH via
the well-known `\?\` long-path prefix (which requires an absolute,
backslash-separated path, so the existing forward-slash paths are
converted first).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@abadams

abadams commented Sep 15, 2026

Copy link
Copy Markdown
Member

Isn't this going to hit other places we access the filesystem too? It just uses the new function for a single call.

@alexreinking

Copy link
Copy Markdown
Member Author

Isn't this going to hit other places we access the filesystem too? It just uses the new function for a single call.

Likely yes, but this fixes the one issue I encountered and could reproduce locally. It would be worth an audit of the other _<posix> filesystem functions we use on Windows.

@abadams

abadams commented Sep 15, 2026

Copy link
Copy Markdown
Member

Other potential issues in the same file are the calls to _access, _stat, and maybe ifstream, ofstream, CreateDirectoryW, and GetTempFileNameW

Shouldn't we just wrap all the filename arguments to all file-system related calls inside WIN32 blocks?

@slomp

slomp commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

FWIW, prefixing a Windows path with \\?\ will bypass the 260 MAX_PATH limit.
https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry

File I/O functions in the Windows API convert "/" to "" as part of converting the name to an NT-style name, except when using the "\?" prefix as detailed in the following sections.

@alexreinking

Copy link
Copy Markdown
Member Author

FWIW, prefixing a Windows path with \\?\ will bypass the 260 MAX_PATH limit.

That's how this PR works

@mcourteaux

mcourteaux commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Windows is ridiculous. We're dealing with limitations introduced 33 years ago... 😞

@codecov

codecov Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.05%. Comparing base (d33fd25) to head (baa9af0).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9452      +/-   ##
==========================================
- Coverage   70.21%   70.05%   -0.16%     
==========================================
  Files         261      261              
  Lines       79792    79940     +148     
  Branches    19451    19478      +27     
==========================================
- Hits        56022    56005      -17     
- Misses      17966    18065      +99     
- Partials     5804     5870      +66     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants