From 24a89e04994f8847272c1771217fc91b3438a853 Mon Sep 17 00:00:00 2001 From: Christian Kaiser <5065635+kaiserc@users.noreply.github.com> Date: Wed, 2 Sep 2026 01:46:05 +0100 Subject: [PATCH] fix(download): disable uTP to prevent ENOBUFS socket exhaustion crashes WebTorrent's uTP transport uses utp-native, which allocates an independent UDP socket for every outgoing peer connection attempt. Under heavy swarm discovery and peer reconnection churn, Windows rapidly exhausts socket buffer space and dynamic ports (WSAENOBUFS / ENOBUFS: 'no buffer space available'). Furthermore, when binding fails with ENOBUFS, utp-native's UTP.connect emits an 'error' event on an orphaned EventEmitter that has no error listener registered, triggering an unhandled exception that crashes the entire application. Disabling uTP via { utp: false } instructs WebTorrent to connect to peers exclusively via standard TCP. TCP connections are universally supported by all BitTorrent clients, fully error-handled, pooled, and do not exhaust ephemeral UDP socket buffers. --- src/download/engine.test.ts | 15 +++++++++++++++ src/download/engine.ts | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/download/engine.test.ts b/src/download/engine.test.ts index 687340f5..b8fdd899 100644 --- a/src/download/engine.test.ts +++ b/src/download/engine.test.ts @@ -110,4 +110,19 @@ describe("TorrentEngine macOS port-5350 fix (#22)", () => { expect(result?.total).toBe(0); engine.destroy(); }); + + it("passes utp:false so utp-native does not exhaust UDP buffers or crash on unhandled bind errors", async () => { + const { TorrentEngine } = await import("./engine"); + const engine = new TorrentEngine(); + engine.add( + "test-id", + "magnet:?xt=urn:btih:0000000000000000000000000000000000000000", + "/downloads", + {}, + ); + engine.destroy(); + expect(constructorCalls).toHaveLength(1); + expect(constructorCalls[0]).toMatchObject({ utp: false }); + }); }); + diff --git a/src/download/engine.ts b/src/download/engine.ts index d3126e54..790df5cf 100644 --- a/src/download/engine.ts +++ b/src/download/engine.ts @@ -71,7 +71,21 @@ export class TorrentEngine { // the app the moment a download starts. NAT-PMP can never succeed // on macOS because the port is permanently taken, so disable it // and let UPnP handle NAT traversal instead. - const opts = process.platform === "darwin" ? { natPmp: false } : {}; + // + // Disable uTP across all platforms: + // WebTorrent's uTP implementation relies on `utp-native`, which allocates + // an independent UDP socket for every outgoing connection attempt. Under + // peer discovery churn, this rapidly exhausts socket buffer space and + // ephemeral ports (leading to WSAENOBUFS / ENOBUFS: "no buffer space available"). + // Furthermore, when `utp-native` fails to bind, it emits an unhandled 'error' + // on an internal EventEmitter with no listeners, crashing the process as an + // uncaught exception. Disabling uTP forces WebTorrent to use standard TCP + // connections, which are supported by 100% of BitTorrent clients and operate + // reliably without socket buffer exhaustion. + const opts = { + utp: false, + ...(process.platform === "darwin" ? { natPmp: false } : {}), + }; this.client = new WebTorrent(opts); this.client.on("error", () => {}); this.applyThrottle();