From b5bb277570ced29c734559d205ed2b8ec19a498b Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 23 Mar 2026 20:04:43 +0000 Subject: [PATCH 1/2] fix(pdf-server): slice bytes before onDataRange to avoid detached buffer PDF.js transfers the ArrayBuffer to its worker via postMessage, detaching it in the main thread. When the same range is re-requested (common on iOS/WKWebView under memory pressure during scroll/zoom/navigate), the cached Uint8Array now wraps a detached buffer and onDataRange throws 'Buffer is already detached'. Fix: pass result.bytes.slice() so the rangeCache entry stays valid. --- examples/pdf-server/src/mcp-app.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/pdf-server/src/mcp-app.ts b/examples/pdf-server/src/mcp-app.ts index c8281c03..6a59407c 100644 --- a/examples/pdf-server/src/mcp-app.ts +++ b/examples/pdf-server/src/mcp-app.ts @@ -3875,7 +3875,11 @@ async function loadPdfProgressively(urlToLoad: string): Promise<{ requestDataRange(begin: number, end: number) { fetchRange(urlToLoad, begin, end) .then((result) => { - this.onDataRange(begin, result.bytes); + // PDF.js transfers the ArrayBuffer to its worker, detaching it. + // Pass a copy so the rangeCache entry stays valid for re-requests + // (iOS/WKWebView re-requests ranges under memory pressure and + // throws "Buffer is already detached" on the cached original). + this.onDataRange(begin, result.bytes.slice()); }) .catch((err: unknown) => { log.error(`Error fetching range ${begin}-${end}:`, err); From efed982ebb2f827f786fe81f9571eb00bd26701e Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 23 Mar 2026 20:11:51 +0000 Subject: [PATCH 2/2] test(e2e): update pdf-annotations assertion for new display_pdf prompt text The 91880a6d prompt rewrite changed the interact-enabled result text from 'Displaying PDF (viewUUID: ...)' to 'PDF opened. viewUUID: ...'. The e2e test was still asserting the old string. --- tests/e2e/pdf-annotations.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/pdf-annotations.spec.ts b/tests/e2e/pdf-annotations.spec.ts index 8008f559..9d4ab958 100644 --- a/tests/e2e/pdf-annotations.spec.ts +++ b/tests/e2e/pdf-annotations.spec.ts @@ -50,7 +50,7 @@ async function extractViewUUID(page: Page): Promise { const resultText = (await resultContent.textContent()) ?? ""; // Extract viewUUID from the JSON result - // The text content includes: "Displaying PDF (viewUUID: ): ..." + // The text content includes: "PDF opened. viewUUID: " const match = resultText.match(/viewUUID["\s:]+([a-f0-9-]{36})/); if (!match) { throw new Error( @@ -107,7 +107,7 @@ test.describe("PDF Server - Annotations", () => { // (interact action list lives in the tool description, not the runtime result). expect(resultText).toContain("viewUUID"); expect(resultText).toContain("interactEnabled"); - expect(resultText).toContain("Displaying PDF"); + expect(resultText).toContain("PDF opened"); }); test("interact tool is available in tool dropdown", async ({ page }) => {