ReviewOS

home-lang/home

Bun parity: audit Bun.serve bind-error object compatibility

#726
Closed chris opened this 9 days ago ·
9 days ago

Parent #66; discovered during the native service/vendor preparation work in #724.

A private error-object comparison on macOS 27 arm64, with an existing listener on port 3000, shows an observable difference between Home and pinned Bun. Both reject the bind with code: "EADDRINUSE", name: "Error", and syscall: "listen".

FieldHomePinned Bun
messageEADDRINUSE: address already in use, listenFailed to start server. Is port 3000 in use?
errno-480
address0.0.0.0absent
port3000absent

Home SHA256: 91d6b1024f9d4c02c6b7f9089b476d8a0646294f4ed7392e5e3ce549e52db763. Pinned Bun source: 4982b91e3702094330f3be3883354c52b8c01323; executable SHA256: 47a87c1dea30da59c2733cce80118ea59bd338c616c6378f1c7f55e5f2b92c87.

The existing Home implementation in packages/runtime/src/runtime/server/server.zig:onListenFailed deliberately preserves the OS errno and adds TCP address/port metadata. Its normalizeListenErrno regression preserves ACCES and uses ADDRINUSE only when the OS did not supply an error. Audit the pinned API contract and original bind-error fixtures before changing this behavior; do not regress concrete permission/address errors merely to copy a generic message.

The original Elysia test/validator/response.test.ts case handle distinct union calls .listen(3000). It fails in both runtimes while the unrelated PredictHQ development server owns that port. Home's complete 129-file run retained 1,515 passes and one failure; pinned Bun's separate original-file diagnostic retained 27 passes and one failure. The listener and test were not modified, and these results are not a passing full-suite claim. The field comparison is a separate API compatibility audit, not a workaround for the occupied-port failure.

  • Audit pinned Bun's occupied TCP, invalid address, permission and Unix socket failure contracts.
  • Resolve and test the intended error-object compatibility while preserving real OS failures.
  • Revalidate applicable unchanged upstream error fixtures and retain native platform boundaries.

Published evidence: c9fc9bf63 · Native service/preparation manifest, including the original complete run, pinned-Bun diagnostic, retained field comparison and source hashes. Related Windows/listen work remains separate in #437.

9 days ago

Resolved on main by f0327d434.

The implementation now follows Bun's onListenFailed contract while capturing errno immediately so logging and TLS error-queue draining cannot overwrite it:

  • TCP failures use Bun's SystemError shape: generic EADDRINUSE, errno: 0, syscall: "listen", no address/port properties, and the Bun message containing the requested port.
  • Linux retains Bun's native EACCES branch and permission-denied message.
  • Unix failures retain concrete OS errors and the socket path; Bun's SUCCESS fallback is preserved separately.

A fresh macOS arm64 differential covered occupied TCP, a nonlocal TCP address, and a stale Unix path against pinned Bun 4982b91e3. Every error field and own-property set is exact after normalizing only the dynamic occupied port and temporary socket path. This also proves the invalid-address case intentionally maps to Bun's public generic TCP contract rather than exposing Home's former EADDRNOTAVAIL object. The unchanged unix-socket-unlink.test.ts passes 8 tests / 17 assertions with its one original Linux-only skip. The fresh ReleaseFast build passes 46/46 steps.

The Linux permission branch is a direct source port guarded by Bun's same compile-time platform condition; this Darwin run does not claim Linux execution. Broader platform execution remains tracked independently, including Windows/listen work in #437. The larger unchanged node-http.test.ts could not initialize because its existing tunnel package is absent from this checkout, so it is not counted as acceptance evidence.

Sign in to comment on this issue.