GraphiteEditor/Graphite · error
Clipboard API unsupported
Error message
Clipboard API unsupported
What it means
Thrown when navigator.clipboard.read() returns a falsy value — the async clipboard read API is not actually usable. In most unsupported environments (insecure origins, old browsers) navigator.clipboard is undefined, so read() throws a TypeError instead; the catch block's matchMessage table routes both cases (message containing "clipboard-read" or this exact text) to the same "This browser does not support reading from the clipboard" dialog.
Source
Thrown at frontend/src/utility-functions/clipboard.ts:98
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;
}
// Read an image from the clipboard and pass it to the editor to be loaded
const imageType = item.types.find((type) => type.startsWith("image/"));
View on GitHub (pinned to c507b35645)
Solutions
- Serve the app over HTTPS, or use http://localhost / http://127.0.0.1 which are secure contexts
- Upgrade to a browser that supports async clipboard read
- Use Ctrl+V — the paste event delivers clipboard content even where clipboard.read() is unavailable
- Feature-detect before calling and disable the menu paste entry when unsupported
Example fix
// before
const clipboardItems = await navigator.clipboard.read();
if (!clipboardItems) throw new Error("Clipboard API unsupported");
// after: feature-detect before calling
if (!window.isSecureContext || typeof navigator.clipboard?.read !== "function") {
throw new Error("Clipboard API unsupported");
}
const clipboardItems = await navigator.clipboard.read(); Defensive patterns
Strategy: type-guard
Type guard
function supportsClipboardRead(): boolean {
return window.isSecureContext && typeof navigator.clipboard?.read === "function";
} Try / catch
// Route both the explicit throw and the TypeError from a missing API to the same guidance
try {
const items = await navigator.clipboard.read();
} catch (err) {
showDialog(String(err).includes("clipboard") ? unsupportedMessage : String(err));
} Prevention
- Gate the menu paste entry on supportsClipboardRead() and hide it when false
- Never deploy the editor on plain-http origins other than localhost (secure context required)
- Keep the paste event (Ctrl+V) as the primary paste mechanism
When it happens
Trigger: Serving the editor over plain http:// on a non-localhost host — the Clipboard API requires a secure context; browsers without async clipboard read support; calling read() in a webview that never exposes navigator.clipboard.
Common situations: Dev servers exposed to the LAN via an IP address (http://192.168.x.x) instead of localhost; previewing a build over http behind a misconfigured proxy; embedding in webviews or older browsers (async clipboard read needs Chrome/Edge 66+, Firefox 127+, Safari 13.1+).
Related errors
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/43642c7b166c8c8c.
Report an issue: GitHub.