From 9d9e469bffecc0dc835aae6bdd6d5ba1b17cb538 Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Tue, 6 Jan 2026 12:43:50 +0100 Subject: [PATCH] fix: sanitize accessibility snapshot to remove unpaired surrogates Fixes 'no low surrogate in string' API error when page content contains malformed Unicode characters. Uses toWellFormed() on Node.js 20+ with graceful fallback on older versions. --- playwriter/CHANGELOG.md | 6 ++++++ playwriter/package.json | 2 +- playwriter/src/aria-snapshot.ts | 4 +++- playwriter/src/mcp.ts | 6 ++++-- tsconfig.base.json | 1 + 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/playwriter/CHANGELOG.md b/playwriter/CHANGELOG.md index 3433b67..4a61a5c 100644 --- a/playwriter/CHANGELOG.md +++ b/playwriter/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.0.42 + +### Bug Fixes + +- **Fix "no low surrogate in string" API error**: Sanitize accessibility snapshot text using `toWellFormed()` to remove unpaired Unicode surrogates that break JSON encoding for Claude API (requires Node.js 20+ for sanitization, gracefully degrades on older versions) + ## 0.0.41 ### Features diff --git a/playwriter/package.json b/playwriter/package.json index 9e83d7e..b66dd49 100644 --- a/playwriter/package.json +++ b/playwriter/package.json @@ -1,7 +1,7 @@ { "name": "playwriter", "description": "", - "version": "0.0.41", + "version": "0.0.42", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/playwriter/src/aria-snapshot.ts b/playwriter/src/aria-snapshot.ts index e4f9164..1866038 100644 --- a/playwriter/src/aria-snapshot.ts +++ b/playwriter/src/aria-snapshot.ts @@ -128,7 +128,9 @@ export async function getAriaSnapshot({ page }: { page: Page }): Promise() diff --git a/playwriter/src/mcp.ts b/playwriter/src/mcp.ts index 2fade3d..2ecfe22 100644 --- a/playwriter/src/mcp.ts +++ b/playwriter/src/mcp.ts @@ -719,7 +719,9 @@ server.tool( const { page: targetPage, search, contextLines = 10, showDiffSinceLastCall = false } = options if ((targetPage as any)._snapshotForAI) { const snapshot = await (targetPage as any)._snapshotForAI() - const snapshotStr = typeof snapshot === 'string' ? snapshot : JSON.stringify(snapshot, null, 2) + // Sanitize to remove unpaired surrogates that break JSON encoding for Claude API + const rawStr = typeof snapshot === 'string' ? snapshot : JSON.stringify(snapshot, null, 2) + const snapshotStr = rawStr.toWellFormed?.() ?? rawStr if (showDiffSinceLastCall) { const previousSnapshot = lastSnapshots.get(targetPage) @@ -1059,7 +1061,7 @@ async function checkRemoteServer({ host, port }: { host: string; port: number }) if (isConnectionError) { throw new Error( `Cannot connect to remote relay server at ${host}:${port}. ` + - `Make sure 'npx playwriter serve' is running on the host machine.`, + `Make sure 'npx -y playwriter serve' is running on the host machine.`, ) } throw new Error(`Failed to connect to remote relay server: ${error.message}`) diff --git a/tsconfig.base.json b/tsconfig.base.json index f980c52..b00a53c 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -8,6 +8,7 @@ "rootDir": "${configDir}/src", "moduleResolution": "NodeNext", "lib": [ + "es2024", "es2022", "es2017", "es7",