Skip to content

Bun.udpSocket: call the error handler with (socket, error) - #44277

Open
robobun wants to merge 2 commits into
mainfrom
robobun/f16f1e4f/udp-error-handler-socket-first
Open

robobun wants to merge 2 commits into
mainfrom
robobun/f16f1e4f/udp-error-handler-socket-first

Conversation

@robobun

@robobun robobun commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #44274

Problem

  • Bun.udpSocket calls the error handler with the error only. bun-types declares error(socket, error).
  • A handler written from the types gets undefined as error. A read of error.message throws TypeError: undefined is not an object (evaluating 'error.message'), exit code 1.
  • Cause: the argument list &[err] in UDPSocket::call_error_handler (src/runtime/socket/udp_socket.rs:871), from UDP support #7271.

Fix

Background

  • node:dgram runs on Bun.udpSocket. Its adapter turns this callback into the 'error' event.
  • Considered error(error) in the types: it repairs no existing handler. Considered Function.length: bun-types cannot declare it.

Downsides

Notes

The break. bun-types does not change, so a handler with one parameter gets no compile error.

const socket = await Bun.udpSocket({
  socket: {
    error(err) {
      console.log(err.code); // before: "ECONNREFUSED". after: undefined, `err` is the socket
    },
  },
});

Change the handler to error(socket, err). A handler that must run before and after this change can use error(a, b) { const err = b ?? a; }. The release post https://bun.com/blog/bun-v1.3.12 shows the one-parameter form. It lives outside this repository and needs the same edit.

One send from a connected socket to a closed port on Linux (release builds, 3 runs each):

error handler main this PR
none uncaught ECONNREFUSED, exit code 1 the same
error(socket, error), reads error.message TypeError, exit code 1 reads the message
error(socket, error), logs error logs undefined logs the error
error(err), reads err.code "ECONNREFUSED" undefined
error(err), returns on ECONNREFUSED, throws the rest returns throws the socket, exit code 1

The node:dgram 'error' event and a socket with no handler do not change.

Demand. No issue reported this before #44274. The evidence is public code: a GitHub code search for Bun.udpSocket error (2026-09-27, 206 files) has 63 error handlers in 44 repositories.

Direction. #44274 has the two options and the questions for a maintainer. The same mismatch exists for websocket.error of Bun.serve: the docs show (ws, error), the runtime passes (error), and bun-types has no member. #41728 (open, approved) declares error?(error: unknown) there. #36033 changed the runtime there and was closed as stale. #11634 is the report. bun-types declared the UDP handler from the start, which is the difference.

History.

Docs and types.

Measurements (linux-x64, release builds of one checkout, with and without the two source lines).

  • error handler argv: 8/8 rows fail on 1.4.3-canary.1 (argc 1), 8/8 pass on the PR build (argc 2, socket first). this is the socket before and after.
  • instructions per error delivery: error() {} +16, error(socket, error) {} +16 (634 to 650). gdb stepi count from the entry of call_error_handler to its return, at deliveries 500, 501 and 502, 2 runs per build, no spread. valgrind and perf are not installed in the container.
  • allocator entries per error delivery: main 0, PR 0 (same gdb trace).
  • release binary: call_error_handler +4 B (509 B to 513 B), one more movq. .text +0 B. .bun_builtins +9 B (the dgram.ts source). File +0 B (80,827,976 B).
  • on_data / on_drain / on_recv_error / UDPSocketConfig::from_js: 0 differing instructions, sizes unchanged. Host functions +0, codegen rows +0, UDPSocket fields +0.
  • node:dgram: dgram.test.ts 62 pass, 0 fail, and test-dgram-recv-error.js exits 0, with the test files unedited. With a stale one-parameter adapter, 3 tests of dgram.test.ts fail. One of them runs on every platform.
  • bun-types fixture: bun-types.test.ts 22 pass, 0 fail. With both declarations flipped to one parameter, tsc 6.0.2 reports 13 diagnostics in fixture/udp.ts.
  • bun-types package: bun.d.ts +1,406 B. Unpacked +2,734 B, which includes +1,328 B of udp.mdx. Packed +548 B.

Suites run on the debug build.

  • test/js/bun/udp/udp_socket.test.ts: 238 pass, 0 fail.
  • test/js/bun/udp/udp_socket_recv_flags.test.ts: 3 pass, 0 fail. 2 of the 3 fail on 1.4.3-canary.1: the new row, and the Linux ICMP test, whose handler at line 58 had one parameter and was a TS2769 on main.
  • test/js/bun/udp/dgram.test.ts: 62 pass, 0 fail.
  • test/js/node/test/parallel/test-dgram-recv-error.js: exit 0.
  • test/js/bun/module-graph/module-graph-io.test.ts: 26 pass, 1 fail. The failure is "through a CONNECT proxy", a 5 s timeout under the debug build. It passes in 7.7 s with --timeout 120000. The test does not use UDP.

Test notes.

  • The 8 rows are {unconnected, connected} x {data throws, drain throws} x {handlers from udpSocket(), handlers from reload()}.
  • The drain rows send nothing. A new socket reports writable once, so drain runs with no send.
  • The reload() rows call reload({ socket }), the shape that the runtime reads. bun-types rejects it (Bun.udpSocket: reload() reads the handlers from socket, bun-types declares reload(handlers) #44271).
  • The new row in udp_socket_recv_flags.test.ts covers the receive route for a connected socket on Linux and macOS. A socket that is connected to another peer holds the target port. The kernel refuses the datagrams, and no other process can bind the port during the test.

Not in this PR.

Open PRs with the one-parameter form.


no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/udp/udp_socket_recv_flags.test.ts, test/js/bun/udp/udp_socket.test.ts

bun-types declares `error(socket, error)` for the handlers of
Bun.udpSocket. The runtime passed the error as the only argument.

`UDPSocket::call_error_handler` now passes the socket first, as `data`
and `drain` do and as the handlers of Bun.listen and Bun.connect do.
The node:dgram adapter takes the error from the second argument, so
the 'error' event of a dgram socket does not change.

A handler written as `error(err)` now receives the socket as `err`.
@robobun
robobun requested a review from alii as a code owner September 30, 2026 04:15
@robobun

robobun commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

Status

Reproduced on Bun 1.4.3-canary.1+367d939d9 and on a release build of main (Linux x64):

const server = await Bun.udpSocket({
  hostname: "127.0.0.1",
  socket: {
    data() {
      throw new Error("boom");
    },
    error(...args) {
      console.log(args.map(arg => Object.prototype.toString.call(arg)));
      server.close();
      client.close();
    },
  },
});
const client = await Bun.udpSocket({ hostname: "127.0.0.1" });
client.send("x", server.port, "127.0.0.1");

Both print [ "[object Error]" ]. A build of this branch prints [ "[object UDPSocket]", "[object Error]" ].

Tests: USE_SYSTEM_BUN=1 bun test test/js/bun/udp/udp_socket.test.ts -t "error handler is called" fails 8 of 8 rows on 1.4.3-canary.1. bun bd test on this branch passes them.

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: oven-sh/bun/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: f6507760-55b8-460c-9dd7-0cb3a3b258a7

📥 Commits

Reviewing files that changed from the base of the PR and between 187e4ce and beb7c88.

📒 Files selected for processing (4)
  • docs/runtime/networking/udp.mdx
  • packages/bun-types/bun.d.ts
  • test/integration/bun-types/fixture/udp.ts
  • test/js/bun/udp/udp_socket_recv_flags.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.


Walkthrough

UDP error callbacks now receive the socket as the first argument and the error as the second. The change updates runtime callback invocation, documents error cases, and adds runtime and type tests.

Changes

UDP error callback

Layer / File(s) Summary
Document and type the callback contract
packages/bun-types/bun.d.ts, docs/runtime/networking/udp.mdx, test/integration/bun-types/fixture/udp.ts
The documentation and type tests describe the socket-first callback and cover handler exceptions, receive failures, and callback parameter types.
Pass the socket to the error callback
src/runtime/socket/udp_socket.rs, src/js/node/dgram.ts
The runtime passes the socket before the converted error. The node:dgram error callback accepts the socket argument before the error.
Verify callback arguments and behavior
test/js/bun/udp/udp_socket.test.ts, test/js/bun/udp/udp_socket_recv_flags.test.ts
Runtime tests check callback arguments and this binding when handlers throw. A receive-error test checks ECONNREFUSED and verifies that the socket remains open.

Suggested reviewers: cirospaciari

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to beb7c

UDP error handlers now receive the socket before the error, so existing one-argument handlers need updating. The new contract is documented, and no separate code defect warrants holding the change.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #44274 requires agreement between the UDP runtime and bun-types. The PR changes UDPSocket::call_error_handler to pass (socket, error). It updates the node:dgram adapter to read the secon…
Out of Scope Changes check ✅ Passed The runtime change, node:dgram adapter change, UDP documentation, JSDoc, type fixtures, and UDP tests support the callback-order correction in #44274. The supplied change summary shows no unrelated …
Title check ✅ Passed The title clearly and concisely describes the main change: Bun.udpSocket now calls the error handler with the socket and error arguments.
Description check ✅ Passed The description provides detailed problem, fix, compatibility impact, verification results, test coverage, and known limitations. It does not use the template headings exactly, but it contains the req…

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beyond the inline findings, I checked for missed sibling sites: UDPSocket::call_error_handler is the only place the UDP error argv is built, so all three producers (recv error, data throw, drain throw) get the new (socket, error) shape, and src/js/node/dgram.ts is the only in-tree consumer of the Bun.udpSocket error handler.

Extended reasoning...

The runtime change is a one-line argv edit in src/runtime/socket/udp_socket.rs plus the matching dgram.ts adapter update, with JSDoc, docs, type fixtures and tests; it touches no security-sensitive surface. The user-observable break for one-parameter handlers (already an inline finding) is a direction call a maintainer must make, so this is not approvable by the bot.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread src/runtime/socket/udp_socket.rs
Comment thread test/js/bun/udp/udp_socket_recv_flags.test.ts Outdated
Comment thread test/integration/bun-types/fixture/udp.ts Outdated
The docs section and the JSDoc of `error` now state that earlier
versions of Bun call the handler with the error as the only argument.

The receive-route test holds its refused port with a socket that is
connected to another peer, so no other process can bind the port while
the test runs. The test fails with a message if the socket closes
before it calls `error`.

The type fixture no longer depends on how TypeScript infers the `socket`
parameter of an inline handler on a connected socket.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bun.udpSocket: the error handler gets (error), bun-types declares (socket, error)

1 participant