diff --git a/packages/host/src/server.ts b/packages/host/src/server.ts index 6808db73..091103a1 100644 --- a/packages/host/src/server.ts +++ b/packages/host/src/server.ts @@ -771,9 +771,14 @@ export class FactorioServer extends events.EventEmitter { } handleUdp(channel: string, handler: (data: Buffer) => Promise) { - this.on(`udp-${channel}`, (data) => handler(data).catch((err: Error) => { - this._logger.error(`Error handling udp event:\n${err.stack ?? err.message}`); - })); + // Runs from the socket callback, so synchronous throws must be caught here too + this.on(`udp-${channel}`, async (data) => { + try { + await handler(data); + } catch (err: any) { + this._logger.error(`Error handling udp event:\n${err.stack ?? err.message}`); + } + }); } async _handleIpc(line: Buffer) { diff --git a/test/host/server.js b/test/host/server.js index 1ffa8973..a3cc96a2 100644 --- a/test/host/server.js +++ b/test/host/server.js @@ -471,6 +471,20 @@ describe("host/server", function() { assert.equal(lines.length, 1); assert(lines[0].startsWith("Error handling udp event:\nError: boom"), lines[0]); }); + it("should log errors thrown synchronously by handleUdp handlers", async function() { + await server._startUdp(); + server.handleUdp("sync", () => { throw new Error("sync boom"); }); + const { lines, restore } = captureLogger("error"); + try { + sendFromGame("sync?data"); + while (lines.length < 1) { + await wait(1); + } + } finally { + restore(); + } + assert(lines[0].startsWith("Error handling udp event:\nError: sync boom"), lines[0]); + }); it("should forward socket errors", async function() { await server._startUdp(); let waiter = events.once(server, "error");