Repository navigation
Python: report an over-long Windows path as ENAMETOOLONG, not a missing file - #10202
Simin Yuan (simin-yuan) wants to merge 3 commits into
Conversation
…ng file On Windows without long-path support, FileSystemAgentFileStore.write() reported a path that merely exceeded the OS limit as FileNotFoundError, which reads as a missing file and sends callers looking for a bug in their own path handling. The limit is now used only to explain an error the OS already raised: when write() fails and a prefix of the path that is still inside the limit exists, the failure is re-raised as OSError(errno.ENAMETOOLONG), chained to the original error. Paths are never rejected up front, so a host with long-path support keeps its current behaviour and every in-limit path is untouched. Directories use the 248-character directory limit, files the 260-character one. The within-bound half of test_filesystem_store_matches_in_memory_complexity_bound now expects ENAMETOOLONG on a Windows host with long paths disabled. New tests cover both call sites with a pinned platform flag, check that unrelated FileNotFoundErrors still propagate unchanged, and check that a path past the limit is still written and read when the host supports long paths. Fixes microsoft#10141
There was a problem hiding this comment.
🟡 Changes recommended
The long-path test can fail depending on the temporary-directory location, while error normalization masks collision and permission failures and incorrectly counts Windows path lengths.
3 open issues
What changed in this PR
This PR addresses #10141 by translating over-long Windows path failures during FileSystemAgentFileStore.write() into ENAMETOOLONG, helping callers distinguish path-length limits from missing files.
Changes:
- Adds error normalization using separate Windows file and directory path limits.
- Documents platform-specific differences between filesystem and in-memory stores.
- Adds regression tests for write failures, successful long-path writes, and unrelated missing-file errors.
| File | Description |
|---|---|
python/packages/core/tests/core/test_harness_file_access.py |
Adds Windows path-limit tests and adjusts backend compatibility expectations. |
python/packages/core/agent_framework/_harness/_file_access.py |
Adds write-error normalization and documents Windows path limits. |
Review effort: Balanced
Count Windows path limits in UTF-16 code units instead of Python code points, restrict the ENAMETOOLONG rewrite to FileNotFoundError so collisions and permission failures keep their own types, and size the regression test's file name from the temporary directory so the path always crosses MAX_PATH.
bc4f2e3 to
b2fd616
Compare
|
Eduard van Valkenburg (@eavanvalkenburg) thanks for the detailed review — all three points are addressed in b2fd616.
Local verification on Windows 11, Python 3.12, One thing I cannot do from the fork: the workflow runs for b2fd616 are sitting in |
|
Eduard van Valkenburg (@eavanvalkenburg) all three points are addressed in
I verified these against the head file rather than only the diff; I did not re-run the Windows test suite locally on this machine for this commit. If any of the three still differs from what you intended, say which and I'll reopen it. |


Motivation & Context
Fixes #10141. On Windows without long-path support,
FileSystemAgentFileStore.write()reports a key whose absolute path is past the OS limit asFileNotFoundError, which reads as "the file is missing" and sends callers looking for a bug in their own path handling.What the OS actually raises depends on the call site. Measured on Windows 11 23H2 (OS build 22631.6199,
LongPathsEnabled=0, Python 3.11.16,mainat 13956ad), with the store rooted at a 9-character path:write()todayOSError(38) ENAMETOOLONGFileNotFoundError, errno 2, nowinerrorOSError(38) ENAMETOOLONGFileNotFoundError, errno 2, WinError 3OSError(38) ENAMETOOLONGFileNotFoundError, errno 2, nowinerrorOSError(38) ENAMETOOLONGFileNotFoundError, errno 2, WinError 3The
write()column is what this patch produces; the right-hand column is the exception chained as__cause__, i.e. what the OS raised before the rewrite.ERROR_FILENAME_EXCED_RANGE(206) does not appear for the callswrite()makes:Path.mkdir(parents=True)surfaces WinError 3 andos.opensurfaces none. That is why the detection here keys on the resolved path length next to an error that already happened, rather than on a particularwinerror.Description & Review Guide
except OSErrorblocks in_write_file_syncnow call_raise_if_path_too_long(exc, path, *, limit), which re-raises the failure asOSError(errno.ENAMETOOLONG)chained to the original error when the path is at or past the Windows limit and a prefix inside the limit exists. Directories use 248 characters and files 260, from the documentedMAX_PATHrules._WINDOWS_PATH_LIMITS_APPLYpinssys.platform == "win32"so the tests can exercise both branches on any CI OS.exceptblock, never before the call, so a host with long-path support behaves exactly as it does today and every in-limit path is untouched.write()only.read(),delete(),list_children()andfile_exists()still report an over-long path as absent — whether they should raise instead, and whether\\?\should be used rather than raising, are your call (asked in the issue).Testing
python/packages/core/tests/core/test_harness_file_access.py→ 148 passed, 8 skipped on Windows 11 withLongPathsEnabled=0.test_filesystem_store_normalizes_windows_path_too_long_error[mkdir|open]— the failure is injected at both call sites and the platform flag is pinned, so it runs on every CI OS.test_filesystem_store_never_rejects_a_path_past_the_windows_limit— a key past the limit must still be written and read when the host supports long paths. This is the guarantee that a length check applied before the OS call would break; it is skipped on the host used above (that host rejects such paths) and runs on a host with long paths enabled and on Linux.test_filesystem_store_propagates_other_file_not_found_errorsstill passes: an unrelatedFileNotFoundErrorkeeps its type and identity.ruff format --check,ruff check,pyright(withpyrightconfig.tests.json) → clean.Known gap:
LongPathsEnabled=1local run pending. Flipping that switch needs an elevated registry write, which the machine used here does not have. The expectation is that all five shapes above simply succeed on such a host; I will add the measured result to this description once the run is done.Related Issue
Fixes #10141
AI Assistance
AI assistance details: implementation, tests, and this description were drafted with an AI assistant. I ran every verification listed above myself on the Windows machine described.