fix: avoid Windows arg-length limits in git check-attr and Scope::Files checks#390
Merged
Merged
Conversation
generated_paths() passed candidate paths as CLI args to `git check-attr`, batched at 256 per invocation. On repos with many/long paths this batch can total tens of KB, exceeding Windows' ~32KB CreateProcess command-line limit (os error 206, ERROR_FILENAME_EXCED_RANGE) even though it's nowhere near Linux/macOS argv limits — this is exactly what broke for a user on a large, deeply-nested Java monorepo. Switch to `git check-attr --stdin -z`, writing paths to the child's stdin instead of argv. Stdin has no OS command-line-length ceiling, so batching is no longer needed. Paths are written on a separate thread concurrently with reading stdout/stderr, since output can fill the OS pipe buffer well before all paths are written for a large repo, which would deadlock if done sequentially. Adds a regression test with long-enough paths that a single 256-path argv batch would total ~56KB, well over Windows' 32KB limit — this crate's own CI matrix runs `cargo test` on windows-2025, so this test exercises the actual failure mode there (Linux/macOS argv limits are much higher and wouldn't have caught this). Signed-off-by: Gregor Zeitlinger <[email protected]>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates Flint’s git check-attr integration to avoid Windows’ CreateProcess command-line length limit by switching from argv-based batching to streaming paths via stdin, and adds a regression test to ensure the issue doesn’t recur.
Changes:
- Run
git check-attr --stdin -zand write candidate paths to stdin (instead of passing many paths as CLI args). - Add a regression test covering many long paths to exercise the Windows failure mode in CI.
- Add
_typos.tomlallowlist entry for the genuine Win32 constant spellingEXCED.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/files.rs | Switch git check-attr invocation to stdin streaming and add regression test for long-path scenarios. |
| _typos.toml | Prevent typos from “correcting” the genuine Win32 EXCED spelling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…imits
Same bug class as the git check-attr fix in this PR: build_invocations()
substituted {FILES}/{RELFILES} with every matched file joined into a
single command, with no length cap at all (not even a fixed batch
count). On Windows this is worse than the check-attr case, since many
Scope::Files checks are mise shims routed through `cmd.exe /C` by
spawn_command, and cmd.exe's own command-line buffer caps at ~8191
chars — tighter than CreateProcessW's ~32KB.
google-java-format is registered via Check::files with no full_cmd
fallback, and opentelemetry-java-instrumentation (where the check-attr
bug was found) has thousands of .java files with long nested paths —
a PR touching as few as ~50 matching files could already overflow the
cmd.exe limit. ryl, zizmor, xmllint, typos, and editorconfig-checker
are similarly exposed. ktlint and dotnet-format have a full_cmd
fallback, but it only applies to whole-project ("full") runs, not the
common PR-scoped changed-files case.
Adds chunk_files_by_length(), splitting matched files into groups that
keep the rendered {FILES}/{RELFILES} argument under a conservative cap
(6000 chars), issuing multiple invocations instead of one unbounded
one. run_invocations() already loops over a list of invocations and
combines their output, so no changes were needed there.
Regression test constructs a Scope::Files check with enough long-named
files that a single invocation would exceed the cap, and verifies
build_invocations splits into multiple invocations that together
cover every matched file exactly once.
Signed-off-by: Gregor Zeitlinger <[email protected]>
- Always join the stdin-writer thread in generated_paths(), even when wait_with_output() errors, so it's never left detached. - Rewrite the check-attr regression test to call generated_paths() directly against synthetic (non-existent-on-disk) long paths instead of real files, avoiding Windows' legacy 260-char MAX_PATH limit on CI runners while still exercising the actual git check-attr subprocess with long paths via stdin. Signed-off-by: Gregor Zeitlinger <[email protected]>
matt-hensley
approved these changes
Jul 11, 2026
martincostello
approved these changes
Jul 13, 2026
Merged
zeitlinger
pushed a commit
that referenced
this pull request
Jul 13, 2026
## 🤖 New release * `flint`: 0.22.7 -> 0.22.8 <details><summary><i><b>Changelog</b></i></summary><p> <blockquote> ## [0.22.8](v0.22.7...v0.22.8) - 2026-07-13 ### Fixed - avoid Windows arg-length limits in git check-attr and Scope::Files checks ([#390](#390)) ### Other - *(deps)* lock file maintenance ([#397](#397)) - *(deps)* update dependency mise to v2026.7.5 ([#395](#395)) - *(deps)* update linters ([#396](#396)) - *(deps)* update taiki-e/install-action action to v2.83.0 ([#394](#394)) - *(deps)* update dependency rust to v1.97.0 ([#392](#392)) - *(deps)* update taiki-e/install-action action to v2.82.11 ([#391](#391)) - *(deps)* update dependency go to v1.26.5 ([#389](#389)) - *(deps)* update taiki-e/install-action action to v2.82.10 ([#388](#388)) - *(deps)* update taiki-e/install-action action to v2.82.9 ([#387](#387)) </blockquote> </p></details> --- This PR was generated with [release-plz](https://github.com/release-plz/release-plz/). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
zeitlinger
added a commit
to zeitlinger/opentelemetry-java-instrumentation
that referenced
this pull request
Jul 13, 2026
v0.22.8 includes grafana/flint#390, fixing `mise run lint:fix` crashing on Windows with "The filename or extension is too long" (os error 206) in git check-attr and Scope::Files checks (google-java-format, ryl, zizmor, xmllint, typos, editorconfig-checker). Signed-off-by: Gregor Zeitlinger <[email protected]>
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
Found by @trask on Windows: open-telemetry/opentelemetry-java-instrumentation#19183 (comment)
Two fixes for the same underlying bug class — passing many/long file paths as CLI args with no (or an inadequate) length cap, overflowing Windows' command-line length limits.
1.
git check-attr(internal git-plumbing helper)generated_paths()passed candidate paths as CLI args togit check-attr, batched at 256 per invocation. On repos with many/long paths (e.g.opentelemetry-java-instrumentation, a deeply-nested Java monorepo) a batch can total tens of KB, exceeding Windows' ~32KBCreateProcesscommand-line limit (os error 206,ERROR_FILENAME_EXCED_RANGE— yes, that's the real Win32 constant's actual spelling; added a_typos.tomlentry sotyposstops "fixing" it to "EXCEED").git check-attr --stdin -z, writing paths to the child's stdin instead of argv — stdin has no OS command-line-length ceiling, so batching is no longer needed. Paths are written on a separate thread concurrently with reading stdout/stderr, since output can fill the OS pipe buffer before all paths are written, which would deadlock if done sequentially.2.
Scope::Filescheck invocations (general check-execution path)While investigating, found the same bug class in
build_invocations()— worse, actually, since it had no length cap at all (not even a fixed batch count):{FILES}/{RELFILES}was substituted with every matched file joined into one command. ManyScope::Fileschecks are mise shims routed throughcmd.exe /Cbyspawn_command, andcmd.exe's own command-line buffer caps at ~8191 chars — tighter thanCreateProcessW's ~32KB.google-java-format(registered viaCheck::files, nofull_cmdfallback) is directly exposed:opentelemetry-java-instrumentationhas thousands of.javafiles with long nested paths, so a PR touching as few as ~50 matching files could already overflow the limit.ryl,zizmor,xmllint,typos, andeditorconfig-checkerare similarly exposed.ktlint/dotnet-formathave afull_cmdfallback, but it only applies to whole-project ("full") runs, not the common PR-scoped changed-files case.chunk_files_by_length(), splitting matched files into groups that keep the rendered{FILES}/{RELFILES}argument under a conservative 6000-char cap, issuing multiple invocations instead of one unbounded one.run_invocations()already loops over a list of invocations and combines their output, so no changes were needed there.Test plan
check-attr: regression test with long-enough paths that a single 256-path argv batch would total ~56KB (verified mathematically, comfortable margin over the 32KB limit)Scope::Files: regression test constructing a check with enough long-named files that a single invocation would exceed the cap, verifyingbuild_invocationssplits into multiple invocations that together cover every matched file exactly once.github/workflows/test.yml) already runscargo testonwindows-2025, so these tests exercise the real failure mode there — can't reproduce the literal Windows OS error from a Linux sandbox, so margins were verified by calculation rather than direct reprocargo build,cargo test(272/273 pass; the one failure is a pre-existing environment gap — missing linter binaries on PATH in this sandbox, unrelated)cargo fmt --check,cargo clippy --all-targets -- -D warningsmise exec -- typos .clean