Skip to content

feat(wasi): normalize wasi error codes and exit-code propagation - #13625

Draft
eduardomourar wants to merge 7 commits into
uutils:mainfrom
eduardomourar:feat/wasi-error-handling
Draft

feat(wasi): normalize wasi error codes and exit-code propagation#13625
eduardomourar wants to merge 7 commits into
uutils:mainfrom
eduardomourar:feat/wasi-error-handling

Conversation

@eduardomourar

@eduardomourar eduardomourar commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Continuation of #12653. Aligns error handling and exit codes on wasm32-wasip1/wasm32-wasip2 with native Unix behavior, and enables the wasip2 integration test suite.

  • Adds uucore::error::process_exit, used everywhere std::process::exit was called, so nonzero exit codes propagate correctly on wasip2 (behind an opt-in wasip2_exit_with_code feature; off by default since it requires a WASI host that supports the unstable exit-with-code function, e.g. wasmtime -S cli-exit-with-code=y).
  • Adds wasi_normalize_open_error/wasi_normalize_read_error/wasi_is_broken_pipe helpers to uucore::error and applies them across cat, rm, wc, csplit, tee, sort, seq, tr, tsort, split, and yes, so directory/broken-pipe errors surface the same ErrorKind and message as on Unix instead of WASI's raw, differently-numbered errnos.
  • Reworks uucore::fs::FileInformation to use real inode/device stat via rustix on WASI instead of a size/type heuristic.
  • Fixes WASI-specific gaps in cp (symlinks, attribute preservation), touch (timestamps), date (clock resolution, set-date), and sort (single-threaded ordering check, since WASI has no threads).
  • Adds/retargets integration tests across most utilities to cover the above, tags WASI-incompatible tests with #[cfg_attr(wasi_runner/wasip2_runner, ignore = "...")], and enables the wasip2 integration run in CI (previously wasip1-only).

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/tail/tail-n0f (fails in this run but passes in the 'main' branch)
Note: The gnu test tests/cut/cut-huge-range is now being skipped but was previously passing.
Note: The gnu test tests/seq/seq-epipe is now being skipped but was previously passing.
Congrats! The gnu test tests/unexpand/bounded-memory is now passing!

@oech3

oech3 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Can we avoid introducing many code with match and cfg, and introduce a fn like process_wasi_err(e)?

@codspeed-hq

codspeed-hq Bot commented Jul 29, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 344 untouched benchmarks
⏩ 47 skipped benchmarks1


Comparing eduardomourar:feat/wasi-error-handling (d311b90) with main (d99a177)

Open in CodSpeed

Footnotes

  1. 47 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@eduardomourar
eduardomourar force-pushed the feat/wasi-error-handling branch from ecc7c96 to 650e875 Compare July 30, 2026 18:09
@eduardomourar eduardomourar changed the title feat(wasi): convert wasi to posix error codes feat(wasi): normalize wasi error codes and exit-code propagation Jul 30, 2026
@eduardomourar

Copy link
Copy Markdown
Contributor Author

Can we avoid introducing many code with match and cfg, and introduce a fn like process_wasi_err(e)?

I had to create 3 separate functions to deal with normalizing the WASI errors, but I think it does what you asked.

@eduardomourar
eduardomourar force-pushed the feat/wasi-error-handling branch 2 times, most recently from f0f8959 to 60f692a Compare July 31, 2026 00:22
On wasm32-wasip2, std::process::exit goes through WASI's stable
wasi:cli/exit#exit, which only carries a success/failure bit and
collapses every nonzero exit code to 1. This adds
uucore::error::process_exit and an opt-in wasip2-exit-with-code
feature that, when enabled, instead calls the unstable
wasi:cli/exit#exit-with-code so real exit codes propagate; the
feature requires a WASI host that explicitly supports it (e.g.
wasmtime's -S cli-exit-with-code=y) and is off by default.
Route every direct std::process::exit call in the multi-call binary,
uucore's #[uucore::main] macro, and per-util fatal-exit paths (cat,
more, shred, sort, tail, tr, tty) through the new process_exit
wrapper so they pick up correct wasip2 exit-code propagation when the
wasip2-exit-with-code feature is enabled, with no behavior change on
other targets.
WASI reports several conditions with different (or no) ErrorKind than
native Unix: opening/removing a directory surfaces raw EBADF instead
of EISDIR, reading from a directory-backed descriptor surfaces EISDIR
under a different errno per preview version, and preview2 reports a
broken pipe as raw EIO. This adds wasi_normalize_open_error,
wasi_normalize_read_error, and wasi_is_broken_pipe to uucore::error so
call sites can convert these to the same ErrorKind/behavior used on
Unix; all three are no-ops off WASI. Also fixes
read_os_string_lines to return an io::Error on invalid UTF-8 instead
of panicking, since argv/filenames aren't guaranteed valid UTF-8 on
WASI.
Route open/read errors through the new uucore::error normalization
helpers so cat, rm, wc, csplit, tee, sort, seq, tr, tsort, and split
report the same ErrorKind/message on WASI as on native Unix: opening
or reading a directory now surfaces IsADirectory instead of a raw
EBADF/EISDIR errno, and seq's broken-pipe short-circuit also catches
preview2's raw EIO. rm and tsort gain small WASI-specific fallbacks
(readability probing via read_dir, directory detection via stat)
where the platform lacks the Unix APIs those checks normally use.
FileInformation previously fell back to a std::fs::Metadata-based
heuristic (comparing file type and size) on WASI, since it lacked
access to nix::sys::stat. Switch it to rustix::fs::Stat, the same
backing type used on Unix, giving WASI real inode/device identity,
nlink, and equality checks instead of an approximation. Also add
WASI implementations of are_hardlinks_to_same_file and
are_hardlinks_or_one_way_symlink_to_same_file via rustix::fs::stat/
lstat, since std::os::unix::fs::MetadataExt is unavailable there.
Several per-util operations relied on APIs unavailable or nightly-only
on WASI: cp now treats ENOSYS like EOPNOTSUPP when skipping optional
attribute preservation (WASI has no chmod/chown at all), reads
timestamps via Metadata::accessed/modified instead of the filetime
crate (which panics there), creates symlinks via rustix::fs::symlink,
and only errors out of --reflink when explicitly requested rather
than whenever it isn't Never. touch applies the same
filetime-avoidance and uses rustix::fs::utimensat for symlink
timestamps. date implements get_clock_resolution and
set_system_datetime for WASI (the latter reporting "not supported",
since the sandbox has no wall-clock-set syscall). sort's ordering
check runs single-threaded on WASI, which has no thread support, and
its external-merge path inserts a missing separator between
concatenated files so lines don't merge across file boundaries.
@eduardomourar
eduardomourar force-pushed the feat/wasi-error-handling branch from 60f692a to 9752428 Compare August 1, 2026 00:41
Adds and retargets integration tests across most utilities to verify
the WASI-specific behavior introduced in prior commits (error codes,
symlink/hardlink handling, cp attribute preservation, sort ordering,
touch timestamps, etc.), and tags tests that can't run under the WASI
sandbox with #[cfg_attr(wasi_runner/wasip2_runner, ignore = "..")]
(host paths, real threads, chmod/chown, and similar gaps). The
uutests harness's symlink helpers now build relative targets under
the WASI runner instead of absolute host paths, and gain a
UUTESTS_WASM_RUNNER_ARGS env var. wasi.yml now runs the wasip2
integration suite (previously wasip1-only) with the
wasip2_exit_with_code feature and wasmtime's cli-exit-with-code flag,
and expands the tested utility list as WASI coverage grows.
docs/src/wasi-test-gaps.md is expanded to catalog every ignore reason.
@eduardomourar
eduardomourar force-pushed the feat/wasi-error-handling branch from 9752428 to d311b90 Compare August 1, 2026 01:22
@eduardomourar
eduardomourar marked this pull request as draft August 1, 2026 08:59
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.

2 participants