From b730056dbc9f1555fabc30ade64ae19d7dddbb88 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 8 Apr 2026 18:17:42 -0700 Subject: [PATCH] fix(run-server): handle malformed JSON frames without crashing A malformed WebSocket text frame caused JSON.parse to throw an uncaught SyntaxError from the async message listener, terminating the run-server process. Catch the parse error, close the offending connection with code 1007, and keep the server running. --- .../src/remote/playwrightConnection.ts | 9 ++++++++- tests/library/browsertype-connect.spec.ts | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/playwright-core/src/remote/playwrightConnection.ts b/packages/playwright-core/src/remote/playwrightConnection.ts index dbf41ceaa2551..cc1b1de751062 100644 --- a/packages/playwright-core/src/remote/playwrightConnection.ts +++ b/packages/playwright-core/src/remote/playwrightConnection.ts @@ -64,7 +64,14 @@ export class PlaywrightConnection { transport.on('message', async (message: string) => { await lock; const messageString = Buffer.from(message).toString(); - const jsonMessage = JSON.parse(messageString); + let jsonMessage: any; + try { + jsonMessage = JSON.parse(messageString); + } catch (e) { + debugLogger.log('server', `[${this._id}] failed to parse message: ${e}`); + this.close({ code: 1007, reason: 'Malformed message' }); + return; + } if (debugLogger.isEnabled('server:channel')) debugLogger.log('server:channel', `[${this._id}] ${monotonicTime() * 1000} ◀ RECV ${messageString}`); if (debugLogger.isEnabled('server:metadata')) diff --git a/tests/library/browsertype-connect.spec.ts b/tests/library/browsertype-connect.spec.ts index e3eb616dce533..d4d3c184b2762 100644 --- a/tests/library/browsertype-connect.spec.ts +++ b/tests/library/browsertype-connect.spec.ts @@ -158,6 +158,22 @@ for (const kind of ['launchServer', 'run-server'] as const) { } }); + test('should not crash on malformed json frame', async ({ connect, startRemoteServer }) => { + const remoteServer = await startRemoteServer(kind); + const ws = new WebSocket(remoteServer.wsEndpoint()); + await new Promise((resolve, reject) => { + ws.once('open', resolve); + ws.once('error', reject); + }); + ws.send(']'); + await new Promise(resolve => ws.once('close', () => resolve())); + // Server should still be alive and accept new connections. + const browser = await connect(remoteServer.wsEndpoint()); + const page = await browser.newPage(); + expect(await page.evaluate('1 + 2')).toBe(3); + await browser.close(); + }); + test('should be able to visit ipv6', async ({ connect, startRemoteServer, ipV6ServerPort, channel }) => { test.fail(!!process.env.INSIDE_DOCKER, 'docker does not support IPv6 by default'); test.fail(channel === 'webkit-wsl', 'WebKit on WSL does not support IPv6: https://github.com/microsoft/WSL/issues/10803');