fix: catch errors in controller message handler

This commit is contained in:
Nicolas Meienberger 2026-05-07 17:29:53 +02:00 committed by Nico
parent 87394d690c
commit 1103fcd2fd
2 changed files with 63 additions and 1 deletions

View file

@ -95,3 +95,56 @@ test("closes the websocket when an outbound send throws", async () => {
session.close();
}
});
test("continues processing inbound messages after a volume command fails", async () => {
const outboundMessages: string[] = [];
const session = createControllerSession(
fromPartial({
send: (message: string) => {
outboundMessages.push(message);
},
}),
);
try {
session.onMessage(
createControllerMessage("volume.command", {
commandId: "command-1",
command: {
name: "filesystem.browse",
path: "/path/that/does/not/exist",
},
}),
);
session.onMessage(createControllerMessage("heartbeat.ping", { sentAt: 123 }));
await waitForExpect(() => {
const parsedMessages = outboundMessages.map((message) => parseAgentMessage(message));
const volumeResult = parsedMessages.find(
(message) => message?.success && message.data.type === "volume.commandResult",
);
const heartbeatPong = parsedMessages.find(
(message) => message?.success && message.data.type === "heartbeat.pong",
);
expect(volumeResult?.success).toBe(true);
if (!volumeResult || !volumeResult.success || volumeResult.data.type !== "volume.commandResult") {
return;
}
expect(volumeResult.data.payload).toEqual({
commandId: "command-1",
status: "error",
error: "ENOENT: no such file or directory, scandir '/path/that/does/not/exist'",
});
expect(heartbeatPong?.success).toBe(true);
if (!heartbeatPong || !heartbeatPong.success || heartbeatPong.data.type !== "heartbeat.pong") {
return;
}
expect(heartbeatPong.data.payload).toEqual({ sentAt: 123 });
});
} finally {
session.close();
}
});

View file

@ -120,7 +120,16 @@ export const createControllerSession = (ws: WebSocket): ControllerSession => {
return;
}
yield* handleControllerCommand(commandContext, parsed.data);
const commandEffect: Effect.Effect<unknown, unknown, never> = handleControllerCommand(
commandContext,
parsed.data,
);
yield* commandEffect.pipe(
Effect.catchAll((error) =>
Effect.sync(() => logger.error(`Failed to handle controller message: ${toMessage(error)}`)),
),
);
}),
),
);