GraphiteEditor/Graphite · error
Permission denied
Error message
Permission denied
What it means
Raised in triggerClipboardRead (the menu-driven paste flow) when navigator.permissions.query({ name: "clipboard-read" }) reports state "denied" — the user or browser policy has blocked clipboard reads for the origin. The surrounding catch maps this exact message to a dialog instructing the user to re-enable the permission in the browser's site settings.
Source
Thrown at frontend/src/utility-functions/clipboard.ts:94
range.setStartAfter(textNode);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
}
element.dispatchEvent(new Event("input", { bubbles: true }));
}
export async function triggerClipboardRead(editor: EditorWrapper) {
// In the try block, attempt to read from the Clipboard API, which may not have permission and may not be supported in all browsers
// In the catch block, explain to the user why the paste failed and how to fix or work around the problem
try {
// Attempt to check if the clipboard permission is denied, and throw an error if that is the case
// In Firefox, the `clipboard-read` permission isn't supported, so attempting to query it throws an error
// In Safari, the entire Permissions API isn't supported, so the query never occurs and this block is skipped without an error and we assume we might have permission
const permission = await navigator.permissions?.query({ name: "clipboard-read" });
if (permission?.state === "denied") throw new Error("Permission denied");
// Read the clipboard contents if the Clipboard API is available
const clipboardItems = await navigator.clipboard.read();
if (!clipboardItems) throw new Error("Clipboard API unsupported");
// Read any layer data or images from the clipboard
const success = await Promise.any(
Array.from(clipboardItems).map(async (item) => {
// Read plain text and, if it is a layer, pass it to the editor
if (item.types.includes("text/plain")) {
const blob = await item.getType("text/plain");
const reader = new FileReader();
reader.onload = () => {
if (typeof reader.result === "string") editor.pasteText(reader.result);
};
reader.readAsText(blob);
return true;
}View on GitHub (pinned to c507b35645)
Solutions
- Re-enable clipboard read via the browser's site settings (icon just left of the URL bar) — exactly what the error dialog instructs
- For enterprise-managed browsers, have the admin add the site to the ClipboardReadWrite allowlist policy
- Use Ctrl+V instead — the paste event does not depend on this permission
- For iframe-embedded apps, add allow="clipboard-read" to the host iframe
Example fix
// before
const permission = await navigator.permissions?.query({ name: "clipboard-read" });
if (permission?.state === "denied") throw new Error("Permission denied");
// after: treat a throwing query (Firefox) as "unknown" instead of conflating it with denial
let state = "prompt";
try {
state = (await navigator.permissions?.query({ name: "clipboard-read" }))?.state ?? "prompt";
} catch { /* Firefox throws on this query name; assume not denied */ }
if (state === "denied") throw new Error("Permission denied"); Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: is clipboard read allowed before attempting the paste menu action?
async function clipboardReadAllowed(): Promise<boolean> {
try {
const p = await navigator.permissions?.query({ name: "clipboard-read" as PermissionName });
return p?.state !== "denied";
} catch {
return true; // Firefox throws on this query name; unsupported does not mean denied
}
} Try / catch
// Keep the existing message-based mapping in the catch so any thrown variant still maps to the right dialog
const message = Object.entries(matchMessage).find(([key]) => String(err).includes(key))?.[1] || String(err);
editor.errorDialog("Cannot access clipboard", message); Prevention
- Only invoke clipboard reads from a real user gesture (menu click)
- Treat a permissions.query() throw as "unknown", never as "denied" (Firefox behavior)
- Keep the Ctrl+V paste-event path available as the permission-independent fallback
When it happens
Trigger: The user clicked "Block" on the clipboard permission prompt or disabled clipboard read in site permission settings; enterprise policy (e.g. Chrome's ClipboardReadWrite URL allowlist / DefaultClipboardSetting) denies read for the origin.
Common situations: Sites embedded in iframes missing allow="clipboard-read"; permission revoked long ago and forgotten; managed/kiosk browsers where the origin is not allowlisted. Mostly a Chromium phenomenon — in Firefox the permissions query itself throws (routed to the "unsupported" dialog) and Safari skips the check entirely.
Related errors
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/fea83e1035353036.
Report an issue: GitHub.