From b01c029b18f9036e53bf83653745f5fef7b33e7e Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Wed, 26 Nov 2025 08:59:33 +0100 Subject: [PATCH] Fix WebSocket upgrade handling to properly route Next.js HMR - Create WebSocketServer with noServer: true to avoid auto-attaching - Manually handle upgrade events to route /ws/script-execution to our WebSocketServer - Route all other WebSocket upgrades (including Next.js HMR) to Next.js handler - This ensures Next.js HMR WebSocket connections are properly handled - Fixes 400 errors for /_next/webpack-hmr WebSocket connections --- server.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index bfa0947..2b61bb0 100644 --- a/server.js +++ b/server.js @@ -79,13 +79,16 @@ class ScriptExecutionHandler { * @param {import('http').Server} server */ constructor(server) { + // Create WebSocketServer without attaching to server + // We'll handle upgrades manually to avoid interfering with Next.js HMR this.wss = new WebSocketServer({ - server, + noServer: true, path: '/ws/script-execution' }); this.activeExecutions = new Map(); this.db = getDatabase(); this.setupWebSocket(); + this.server = server; } /** @@ -1185,6 +1188,23 @@ app.prepare().then(() => { // Create WebSocket handlers const scriptHandler = new ScriptExecutionHandler(httpServer); + + // Manually handle upgrade events to ensure Next.js HMR WebSocket works + // We handle /ws/script-execution ourselves and pass everything else to Next.js + httpServer.on('upgrade', (request, socket, head) => { + const parsedUrl = parse(request.url || '', true); + const { pathname } = parsedUrl; + + if (pathname === '/ws/script-execution') { + // Handle our custom WebSocket endpoint + scriptHandler.wss.handleUpgrade(request, socket, head, (ws) => { + scriptHandler.wss.emit('connection', ws, request); + }); + } else { + // Pass all other WebSocket upgrades (including Next.js HMR) to Next.js handler + handle(request, socket, head); + } + }); // Note: TerminalHandler removed as it's not being used by the current application httpServer