Skip to content

fix(spawnSync): throw instead of segfaulting when event-loop creation fails - #35033

Open
robobun wants to merge 4 commits into
mainfrom
farm/3482e701/spawnsync-loop-init-null
Open

fix(spawnSync): throw instead of segfaulting when event-loop creation fails#35033
robobun wants to merge 4 commits into
mainfrom
farm/3482e701/spawnsync-loop-init-null

address review: platform-appropriate syscall name in spawnSync loop-i…

1d0f5c2
Select commit
Loading
Failed to load commit list.
Claude / Claude Code Review completed Jul 22, 2026 in 22m 27s

Code review found 2 potential issues

Found 2 candidates, confirmed 2. See review comments for details.

Details

Severity Count
🔴 Important 0
🟡 Nit 2
🟣 Pre-existing 0
Severity File:Line Issue
🟡 Nit src/runtime/api/bun/js_bun_spawn_bindings.rs:1081-1087 Windows: extra_fds uv::Pipe allocations leak on the new loop-init-failed early return
🟡 Nit packages/bun-usockets/src/eventing/epoll_kqueue.c:806-808 macOS async cleanup leaks a dead-name right after dropping the receive right

Annotations

Check warning on line 1087 in src/runtime/api/bun/js_bun_spawn_bindings.rs

See this annotation in the file changed.

@claude claude / Claude Code Review

Windows: extra_fds uv::Pipe allocations leak on the new loop-init-failed early return

On Windows, this new early return leaks any `Box<uv::Pipe>` allocations sitting in `extra_fds` (populated by `as_spawn_option()` for stdio slots ≥ 3) — `WindowsStdio` deliberately has no `Drop`, and `spawn_options.deinit()` (which would iterate `extra_fds` and call `.deinit()`) hasn't been reached yet. This is heap-only (~`sizeof(uv_pipe_t)` per slot, never `uv_pipe_init`'d so no OS handle) and the same pattern already exists on several pre-existing early returns in this function, so it needn't 

Check warning on line 808 in packages/bun-usockets/src/eventing/epoll_kqueue.c

See this annotation in the file changed.

@claude claude / Claude Code Review

macOS async cleanup leaks a dead-name right after dropping the receive right

On the `mach_port_set_attributes` failure path, `mach_port_mod_refs(..., MACH_PORT_RIGHT_RECEIVE, -1)` destroys the port but the send right added by `mach_port_insert_right` is converted to a dead-name entry that persists in the task's port name table (the code comment even says so). Add `mach_port_deallocate(self, cb->port);` after the `mod_refs` call to release it — same as `us_internal_async_close` does for the send right on the normal-close path. Essentially-unreachable trigger, but this blo