{"record":{"id":"43642c7b166c8c8c","repo":"GraphiteEditor/Graphite","slug":"clipboard-api-unsupported","errorCode":null,"errorMessage":"Clipboard API unsupported","messagePattern":"Clipboard API unsupported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/utility-functions/clipboard.ts","lineNumber":98,"sourceCode":"\t\tselection.addRange(range);\n\t}\n\n\telement.dispatchEvent(new Event(\"input\", { bubbles: true }));\n}\n\nexport async function triggerClipboardRead(editor: EditorWrapper) {\n\t// In the try block, attempt to read from the Clipboard API, which may not have permission and may not be supported in all browsers\n\t// In the catch block, explain to the user why the paste failed and how to fix or work around the problem\n\ttry {\n\t\t// Attempt to check if the clipboard permission is denied, and throw an error if that is the case\n\t\t// In Firefox, the `clipboard-read` permission isn't supported, so attempting to query it throws an error\n\t\t// 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\n\t\tconst permission = await navigator.permissions?.query({ name: \"clipboard-read\" });\n\t\tif (permission?.state === \"denied\") throw new Error(\"Permission denied\");\n\n\t\t// Read the clipboard contents if the Clipboard API is available\n\t\tconst clipboardItems = await navigator.clipboard.read();\n\t\tif (!clipboardItems) throw new Error(\"Clipboard API unsupported\");\n\n\t\t// Read any layer data or images from the clipboard\n\t\tconst success = await Promise.any(\n\t\t\tArray.from(clipboardItems).map(async (item) => {\n\t\t\t\t// Read plain text and, if it is a layer, pass it to the editor\n\t\t\t\tif (item.types.includes(\"text/plain\")) {\n\t\t\t\t\tconst blob = await item.getType(\"text/plain\");\n\t\t\t\t\tconst reader = new FileReader();\n\t\t\t\t\treader.onload = () => {\n\t\t\t\t\t\tif (typeof reader.result === \"string\") editor.pasteText(reader.result);\n\t\t\t\t\t};\n\t\t\t\t\treader.readAsText(blob);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Read an image from the clipboard and pass it to the editor to be loaded\n\t\t\t\tconst imageType = item.types.find((type) => type.startsWith(\"image/\"));\n","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/frontend/src/utility-functions/clipboard.ts#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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+).","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"],"exampleFix":"// before\nconst clipboardItems = await navigator.clipboard.read();\nif (!clipboardItems) throw new Error(\"Clipboard API unsupported\");\n\n// after: feature-detect before calling\nif (!window.isSecureContext || typeof navigator.clipboard?.read !== \"function\") {\n\tthrow new Error(\"Clipboard API unsupported\");\n}\nconst clipboardItems = await navigator.clipboard.read();","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function supportsClipboardRead(): boolean {\n\treturn window.isSecureContext && typeof navigator.clipboard?.read === \"function\";\n}","tryCatchPattern":"// Route both the explicit throw and the TypeError from a missing API to the same guidance\ntry {\n\tconst items = await navigator.clipboard.read();\n} catch (err) {\n\tshowDialog(String(err).includes(\"clipboard\") ? unsupportedMessage : String(err));\n}","preventionTips":["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"],"tags":["clipboard","secure-context","browser-support","paste"],"backgroundTag":"clipboard-api-unavailable","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}