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();