Repository navigation
Commit 7ad0804
Stop counting macOS setTypeOfService EINVAL as a test failure (#389)
## Problem
Full-suite runs on macOS sometimes end with one unhandled error,
`setTypeOfService EINVAL`, blamed on `node-target.test.ts`. All tests
pass, but Vitest counts the error, so the run fails.
```
test worker (Node 24, macOS) vp dev (Vite)
---------------------------- -------------
waitForServer: fetch 127.0.0.1:P ---> port check: listen 0.0.0.0:P, then close
handshake done, still queued close() resets the queued connection (RST)
'connect' callback runs
undici 7: socket.setTypeOfService(0)
setsockopt(IP_TOS) -> EINVAL
throws inside the event handler
-> uncaught -> Vitest "1 error" -> run fails
(the fetch itself rejects with ECONNRESET and waitForServer retries)
```
- Node 24 bundles undici 7. On each new connection it calls
`socket.setTypeOfService()` with no `try/catch`.
- On macOS, `setsockopt(IP_TOS)` returns EINVAL once the peer has reset
the socket. Linux returns 0, and Node ignores the error on Windows.
- Vite checks that the port is free by briefly listening on `0.0.0.0`
and `::`. If the test polls during that moment, its connection gets
reset.
- Upstream: nodejs/undici#5544. It is fixed in
undici 8.8+, but undici 7 (up to 7.30.0, used by Node 24) still has the
bug.
## Fix
Add a root `test.onUnhandledError` filter that drops only errors with
`syscall: 'setTypeOfService'` and `code: 'EINVAL'`. This does the same
thing as the upstream fix: it treats the failed call as best-effort. The
fetch still rejects, so callers see the failure as before. Other
unhandled errors are still reported.
We can remove the filter once Node 24 ships an undici with the fix.
## Testing
| Check | Before | After |
| --- | --- | --- |
| Test that forces a reset (10 runs) | 10/10 runs fail | 0/10 fail, 12
errors dropped |
| `node-target.test.ts` with fast polling to widen the race (25 runs) |
6/25 fail with this exact error | 0/25 fail, 4 errors dropped |
| `node-target.test.ts` as-is: 20 runs idle, 40 under CPU load | 0/60
(race is rare at 200 ms polling) | - |
| Real `vp dev` startup with tight fetch polling (30 starts) | 7
uncaught EINVAL | - |
| Control: built Node server, same polling (30 starts) | 0 | - |
| Unrelated uncaught error | reported | still reported |
| Linux (Docker, Node 24.21.0), reset socket | `setsockopt` returns 0 |
- |
| `vp lint`, `vp fmt --check`, `node-target.test.ts` | - | pass |
| `vp test run` (full suite, macOS) | - | pass: 688 files passed, 1
skipped; 13062 tests passed, 29 skipped |
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>1 parent a76befe commit 7ad0804
1 file changed
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
399 | 399 | | |
400 | 400 | | |
401 | 401 | | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
402 | 411 | | |
403 | 412 | | |
404 | 413 | | |
| |||
0 commit comments