Skip to content

sort: match GNU's error for a bad --parallel value - #14303

Closed
arbelonson-source wants to merge 1 commit into
uutils:mainfrom
arbelonson-source:fix/sort-parallel-error-message
Closed

sort: match GNU's error for a bad --parallel value#14303
arbelonson-source wants to merge 1 commit into
uutils:mainfrom
arbelonson-source:fix/sort-parallel-error-message

Conversation

@arbelonson-source

Copy link
Copy Markdown
Contributor

Fixes #13016 (message wording; the underlying accept-anything bug that issue reported was already fixed in #13024, but the replacement clap-based validation still doesn't say what GNU says).

--parallel used clap's built-in ranged u64 parser, so a bad value got clap's wording instead of GNU's own:

$ sort --parallel=0 f       # ours, before
error: invalid value '0' for '--parallel <NUM_THREADS>': 0 is not in 1..18446744073709551615
$ sort --parallel=0 f       # GNU 9.11
sort: number in parallel must be nonzero

$ sort --parallel=-1 f      # ours, before
error: invalid value '-1' for '--parallel <NUM_THREADS>': invalid digit found in string
$ sort --parallel=-1 f      # GNU
sort: invalid --parallel argument '-1'

--parallel shares the same number-with-suffix parser -S/--buffer-size already uses via format_error_message, so this reuses that rather than inventing new wording. GNU syntactically accepts a suffix here too (--parallel=2Kinvalid suffix in --parallel argument '2K', not a plain "invalid argument"), but no suffix is ever semantically valid for a thread count, so the allow list passed to the parser is empty rather than the list -S uses.

A value starting with - is still accepted as this option's value, not a new flag — sort --parallel -1 — matching GNU. That needed allow_hyphen_values, which the old value_parser!(u64).range(1..) never had, so --parallel -1 (space-separated, as opposed to --parallel=-1) was already broken before this change; I confirmed that specific breakage on main and it isn't something I introduced.

Deliberately not covered

  • GNU accepts an arbitrarily large --parallel value without erroring, and apparently does not try to spawn that many OS threads for it. uutils' rayon::ThreadPoolBuilder builds its pool eagerly, so asking for anywhere near that many threads hangs rather than completing — a pre-existing limitation already reachable today with an ordinary in-range count (sort --parallel 10000 f already hangs on current main, confirmed before this PR). Clamping an overflowing value the way head/tail/numfmt do elsewhere in this project would make that hang reachable from an even wider set of inputs (including ordinary overflow past u64::MAX), so this keeps the existing behavior of erroring on overflow instead, just with a clearer message. Fixing the hang itself means teaching the thread pool that --parallel is a soft upper bound rather than a literal count to build eagerly for, which felt like a distinct problem from an error-message mismatch.
  • -S/--buffer-size has an existing comment in sort.rs acknowledging that GNU echoes back whichever spelling was actually typed (-S vs --buffer-size), and that this isn't implemented — the message always says --buffer-size regardless. --parallel has no short form, so it doesn't need that distinction, but -S still does; not touched here.

Testing

  • Differential sweep against GNU 9.11 covering --parallel=X and --parallel X (space and = forms) for 0, negative, non-numeric, empty, a suffix, and in-range valid values: 20/21 match; the one that doesn't (an overflowing value) is the deliberate scope boundary above, confirmed GNU itself doesn't error there while uutils does (by design, to avoid the hang).
  • Strengthened the existing test_parallel_invalid from a bare exit-code check to exact GNU-matching messages, plus new cases for negative, empty, a suffix, and the space-separated negative form; verified to fail without the fix.
  • cargo test --features sort --test tests -- test_sort: 211 passed, 0 failed (210 pre-existing — 1 strengthened, not deleted).
  • moz-fluent-lint on src/uu/sort/locales: no errors.
  • cargo fmt --check and cargo clippy -p uu_sort --all-targets -- -D warnings: clean.

Disclosure

Prepared with AI assistance (Claude Opus 5, via Claude Code), per the AI policy in CONTRIBUTING.md. GNU's behaviour was established by running the installed GNU binary as a black box; I did not read GNU coreutils source. All testing was run locally.

`--parallel` used clap's built-in ranged `u64` parser, so a bad value got
clap's wording instead of GNU's own -- the same class of gap fixed for
other options earlier in this project:

    $ sort --parallel=0 f       # ours, before
    error: invalid value '0' for '--parallel <NUM_THREADS>': 0 is not in 1..18446744073709551615
    $ sort --parallel=0 f       # GNU 9.11
    sort: number in parallel must be nonzero

    $ sort --parallel=-1 f      # ours, before
    error: invalid value '-1' for '--parallel <NUM_THREADS>': invalid digit found in string
    $ sort --parallel=-1 f      # GNU
    sort: invalid --parallel argument '-1'

`--parallel` shares the same number-with-suffix parser `-S`/`--buffer-size`
already uses via `format_error_message`, so this reuses that rather than
inventing new wording: GNU syntactically accepts a suffix here too, but
none is ever valid for a thread count, so the allow list passed to the
parser is empty rather than the one `-S` uses.

A value starting with `-` is still accepted as this option's value, not a
new flag -- `--parallel -1` -- matching GNU. This needed
`allow_hyphen_values`, which the old `value_parser!(u64).range(1..)` did
not have, so `--parallel -1` (space-separated) was already broken before
this change; verified that specific breakage pre-dates this PR.

## Deliberately not covered

GNU accepts an arbitrarily large `--parallel` value without erroring, and
apparently does not actually try to spawn that many OS threads for it.
uutils' `rayon::ThreadPoolBuilder` builds its pool eagerly, so asking for
anywhere near that many threads hangs rather than completing -- a
pre-existing limitation, already reachable today with an ordinary
in-range count (`--parallel 10000` already hangs on current `main`).
Rather than clamp an overflowing value the way `head`/`tail`/`numfmt` do
elsewhere in this project (which would make the hang reachable from an
even wider set of inputs, including ordinary overflow past `u64::MAX`),
this keeps the pre-existing behavior of erroring on overflow, just with a
clearer message. Fixing the hang itself means teaching the thread pool
that `--parallel` is a soft upper bound, not a literal thread count to
build eagerly, which felt like a distinct problem from an error-message
mismatch.

Separately, and also not touched here: sort's `-S`/`--buffer-size` has a
comment acknowledging that GNU echoes back whichever spelling, `-S` or
`--buffer-size`, was actually typed, and that this is not yet implemented
(the message always says "--buffer-size"). `--parallel` has no short
form, so this PR does not need that distinction, but `-S` still does.
@oech3

oech3 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

see #14308 (comment)

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/tail/inotify-dir-recreate (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/cut/bounded-memory (passes in this run but fails in the 'main' branch)
Note: The gnu test tests/rm/many-dir-entries-vs-OOM is now being skipped but was previously passing.
Skip an intermittent issue tests/pr/bounded-memory (was skipped on 'main', now failing)

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.

sort: --parallel succeeds on a non-numerical value

3 participants