Log synchronous throws from handleUdp handlers

The listener runs from the socket callback, so a non-async handler that
threw before returning a promise reached the unhandled error handler and
took down the host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 5.5 committed 2026-10-17 10:29:21 +00:00
1 parent fb53845f2a
commit 25c3c9991e
2 files changed
+22 -3

No files matched your search

+8 -3
View File
@@ -771,9 +771,14 @@ export class FactorioServer extends events.EventEmitter<FactorioServerEvents> {
}
handleUdp(channel: string, handler: (data: Buffer) => Promise<void>) {
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) {
+14
View File
@@ -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");