fix(linker): richer diagnostic for subprocess spawn failures (Windows long-cmdline / long-path)#1722
Open
fix(linker): richer diagnostic for subprocess spawn failures (Windows long-cmdline / long-path)#1722
Conversation
When the linker subprocess fails to start, the previous diagnostic was
just `An error occurred during linking: {io::Error}` (E077) — too terse
to act on. Most notably, Windows raises `os error 206`
(`ERROR_FILENAME_EXCED_RANGE`) for two distinct conditions —
`CreateProcess` rejecting an assembled command line longer than 32,767
bytes, or `CreateFile` rejecting a single path past `MAX_PATH` without
the `\\?\` prefix — and we couldn't tell which.
Now the spawn-failure path goes through a `diagnose_spawn_error` helper
that includes:
- linker path
- arg count
- approximate command-line length (sum of arg byte-lengths plus
separators; not a faithful CommandLineToArgvW reimplementation)
- longest individual argument and its length
On Windows additionally, when `raw_os_error() == Some(206)`, the
diagnostic appends a Windows-specific section listing the two possible
causes and two user-side workarounds (move the workspace closer to the
drive root; enable `LongPathsEnabled`).
The schema-level error (`LinkerError::Link(String)` → E077) is
unchanged — only the message string is richer.
Refs #1721
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build Artifacts🐧 Linux
From workflow run 🪟 Windows
From workflow run |
The Windows-only spawn_diagnostic_for_os_error_206_includes_windows_section test passed ["one", "two"]. Both are 3 bytes and max_by_key returns the last element on ties, so the actual longest arg was 'two' while the snapshot asserted 'one', failing the Windows CI lane. Use ["one", "three"] so the longest is unambiguous on every platform. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Refs #1721 — diagnostic step only, not the mitigation.
When the linker subprocess fails to start, the current diagnostic is the flat
An error occurred during linking: {io::Error}(E077). For Windows hosts hittingos error 206(ERROR_FILENAME_EXCED_RANGE) — the user-reported symptom in #1721 — that message is too terse to be actionable, and 206 covers two distinct conditions that can't be disambiguated from the bare error code:CreateProcessrejecting an assembled command line longer than 32,767 bytes (or 8,191 if going throughcmd.exe).CreateFilerejecting a single path longer thanMAX_PATH(260 chars) that is not prefixed with\\?\.This PR adds a small
diagnose_spawn_errorhelper that wraps the failingCommand::status()with structured context. It does not change theLinkerErrorschema (stillLink(String)→ E077), nor does it ship the actual mitigation (response file,\\?\prefix). The mitigation will land separately once field reports show which failure mode dominates — see #1721 for the two-step plan.What this PR does
diagnose_spawn_error(err: &io::Error, linker: &Path, args: &[String]) -> LinkerErrorinsrc/linker.rs.?onCommand::new(...).args(...).status()inCcLinker::finalizeis replaced with explicit error mapping that funnels through the helper.CommandLineToArgvWquoting; the figure is meant to give the user a sense of scale.err.raw_os_error() == Some(206): appends a section listing the two possible causes (cmdline > 32K vs single path > MAX_PATH) and two user-side workarounds (move workspace closer to drive root, enableLongPathsEnabled).Sample output
Linux, generic
PermissionDenied(snapshot-pinned by the new test):Windows,
os error 206(snapshot-pinned by the#[cfg(windows)]test):The Windows test uses an
instafilter on the locale-dependent first line because the underlyingio::Errordisplay is localized byFormatMessage.Tests
spawn_diagnostic_for_generic_io_error— syntheticPermissionDenied+ 3 args; pins the generic message shape.spawn_diagnostic_for_os_error_206_includes_windows_section(#[cfg(windows)]) — syntheticfrom_raw_os_error(206); pins the full Windows-specific message via a locale-tolerant filter.The helper is a pure function over
(io::Error, &Path, &[String]), so the tests don't spawn any subprocess.Out of scope
\\?\extended-path prefix) — Step 2 of Windows: linker fails with "os error 206" when build paths or command line exceed Windows limits #1721.include_pathexist #1215 / PR fix(project): validate file/include paths and reject empty input sets #1713.