From 25df9f2f83eed10fea49c146e7e2cbef91c47e45 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 12 Mar 2026 13:43:21 +0000 Subject: [PATCH] :bug: Fix DataCloneError in plugin postMessage communication Fixes a crash where plugins sending messages via 'penpot.ui.sendMessage()' could fail if their message payload contained non-serializable values like functions or closures. The fix adds validation using 'structuredClone()' to catch these messages early with a helpful error message, and adds a defensive try/catch in the modal's message handler as a safety net. Fixes the error: 'Failed to execute postMessage on Window: ... could not be cloned.' Signed-off-by: Andrey Antukh --- plugins/libs/plugins-runtime/src/lib/api/index.ts | 15 ++++++++++++++- .../plugins-runtime/src/lib/modal/plugin-modal.ts | 9 ++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/plugins/libs/plugins-runtime/src/lib/api/index.ts b/plugins/libs/plugins-runtime/src/lib/api/index.ts index 6d452d2ddc..b24c2328f3 100644 --- a/plugins/libs/plugins-runtime/src/lib/api/index.ts +++ b/plugins/libs/plugins-runtime/src/lib/api/index.ts @@ -68,8 +68,21 @@ export function createApi( }, sendMessage(message: unknown) { + let cloneableMessage: unknown; + + try { + cloneableMessage = structuredClone(message); + } catch (err) { + console.error( + 'plugin sendMessage: the message could not be cloned. ' + + 'Ensure the message does not contain functions, DOM nodes, or other non-serializable values.', + err, + ); + return; + } + const event = new CustomEvent('message', { - detail: message, + detail: cloneableMessage, }); plugin.getModal()?.dispatchEvent(event); diff --git a/plugins/libs/plugins-runtime/src/lib/modal/plugin-modal.ts b/plugins/libs/plugins-runtime/src/lib/modal/plugin-modal.ts index b9a9183c8b..3346175212 100644 --- a/plugins/libs/plugins-runtime/src/lib/modal/plugin-modal.ts +++ b/plugins/libs/plugins-runtime/src/lib/modal/plugin-modal.ts @@ -129,7 +129,14 @@ export class PluginModalElement extends HTMLElement { return; } - iframe.contentWindow.postMessage((e as CustomEvent).detail, '*'); + try { + iframe.contentWindow.postMessage((e as CustomEvent).detail, '*'); + } catch (err) { + console.error( + 'plugin modal: failed to send message to iframe via postMessage.', + err, + ); + } }); this.shadowRoot.appendChild(this.wrapper);