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

  1. Re-enable clipboard read via the browser's site settings (icon just left of the URL bar) — exactly what the error dialog instructs
  2. For enterprise-managed browsers, have the admin add the site to the ClipboardReadWrite allowlist policy
  3. Use Ctrl+V instead — the paste event does not depend on this permission
  4. 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

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.