Desktop preview: navigator.clipboard.writeText() fails with 'Write permission denied' (embedded browser denies clipboard-write)
Summary
In the Claude Code desktop app's built-in app preview, any previewed web app that calls navigator.clipboard.writeText(...) (or clipboard.write(...)) fails. The async Clipboard API exists but its permission is denied by the preview's embedded browser, so the call rejects with:
NotAllowedError: Failed to execute 'writeText' on 'Clipboard': Write permission denied
This is not an issue with the previewed app — it is the preview environment's clipboard permission policy.
Environment
- Claude Code desktop app, built-in preview (MCP
Claude_Preview:preview_start/preview_eval). - Previewed app: Next.js dev server on
http://localhost:<port>. - macOS (Darwin).
Repro
- Run any web app in the desktop preview.
- From a user gesture (button click), call
navigator.clipboard.writeText("hello"). - The promise rejects with
NotAllowedError: ... Write permission denied.
Observed (probed via preview_eval in the live preview)
{
"isTopLevel": true, // top-level context, NOT a nested iframe
"hasFrameElement": false,
"isSecureContext": true, // localhost is a secure context
"writeTextExists": "function", // API present
"permClipboardWrite": "denied" // navigator.permissions.query({name:'clipboard-write'}) -> "denied"
}
Analysis
The context is top-level and secure, so this is not an iframe-delegation problem and cannot be fixed by a Permissions-Policy response header from the previewed app (a document header can only restrict, never grant beyond the embedder). With clipboard-write reported as denied in a top-level Electron/embedded-browser context, the denial originates from the embedder's permission handler (e.g. session.setPermissionRequestHandler / setPermissionCheckHandler) — i.e. inside the Claude Code desktop app, out of the previewed app's control.
Expected
The preview's embedded browser should grant (or at least allow, on user gesture) the clipboard-write permission, so that standard navigator.clipboard.writeText() works during preview testing — matching normal localhost browser behavior.
Suggested fix
In the preview's Electron session, allow clipboard-sanitized-write (and optionally clipboard-read) in the permission request/check handler for previewed localhost origins.
Workaround (for app authors)
Catch the rejection and fall back to a legacy document.execCommand("copy") (works under a real user gesture with focus), then degrade to a toast.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗