{"record":{"id":"f19ac6e19214ee5a","repo":"gitbutlerapp/gitbutler","slug":"clipboard-access-is-unavailable","errorCode":null,"errorMessage":"Clipboard access is unavailable.","messagePattern":"Clipboard access is unavailable\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/but-mcp-app/src/WorkspaceApp.tsx","lineNumber":217,"sourceCode":"\t\ttimeStyle: \"short\",\n\t}).format(date);\n}\n\nasync function copyText(value: string): Promise<void> {\n\tif (navigator.clipboard?.writeText) {\n\t\tawait navigator.clipboard.writeText(value);\n\t\treturn;\n\t}\n\n\tconst input = document.createElement(\"textarea\");\n\tinput.value = value;\n\tinput.style.position = \"fixed\";\n\tinput.style.opacity = \"0\";\n\tdocument.body.appendChild(input);\n\tinput.select();\n\tconst copied = document.execCommand(\"copy\");\n\tinput.remove();\n\tif (!copied) throw new Error(\"Clipboard access is unavailable.\");\n}\n\nfunction GraphSegment({ kind, status }: { kind: \"branch\" | \"commit\"; status: GraphStatus }) {\n\treturn (\n\t\t<span className=\"graph-segment\" data-status={status} aria-hidden=\"true\">\n\t\t\t<svg viewBox=\"0 0 16 28\" fill=\"none\">\n\t\t\t\t{kind === \"commit\" ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<path className=\"graph-rail\" d=\"M8 0V11M8 17V28\" />\n\t\t\t\t\t\t<circle cx=\"8\" cy=\"14\" r=\"3.5\" />\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<path className=\"graph-rail\" d=\"M8 0V28\" />\n\t\t\t\t\t\t<path d=\"M16 14H14C10.6863 14 8 16.6863 8 20\" />\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</svg>","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/packages/but-mcp-app/src/WorkspaceApp.tsx#L199-L235","documentation":"Thrown by copyText in packages/but-mcp-app/src/WorkspaceApp.tsx:217. It first tries navigator.clipboard.writeText; when that API is unavailable it falls back to a hidden textarea plus the deprecated document.execCommand(\"copy\"). If execCommand returns false — the browser refused the programmatic copy — this error is raised and surfaced through the detail error UI.","triggerScenarios":"Insecure context (plain HTTP on a non-localhost host) where navigator.clipboard is undefined and execCommand is denied; iframe embedding without the clipboard-write allow attribute; invoking copy outside a real user gesture; document not focused at copy time.","commonSituations":"Developing over a LAN IP with http://; the app embedded in a webview/iframe; automated or headless environments; Safari quirks that require explicit focus.","solutions":["Serve the app over HTTPS or from localhost so the async Clipboard API is available","Trigger copy from a genuine user gesture (onClick handler) and ensure document.hasFocus()","Add the clipboard-write allow attribute to embedding iframes","On failure, keep the text selected and prompt the user to press Cmd/Ctrl+C manually"],"exampleFix":"// before\nconst copied = document.execCommand(\"copy\");\ninput.remove();\nif (!copied) throw new Error(\"Clipboard access is unavailable.\");\n\n// after — degrade to a manual-copy hint instead of failing\nconst copied = document.execCommand(\"copy\");\ninput.remove();\nif (!copied) {\n\tsetDetailError(\"Automatic copy failed — the value stays selected; press Ctrl/Cmd+C.\");\n\treturn;\n}","handlingStrategy":"validation","validationCode":"function clipboardAvailable(): boolean {\n\tif (navigator.clipboard && window.isSecureContext) return true;\n\treturn (\n\t\ttypeof document.queryCommandSupported === \"function\" &&\n\t\tdocument.queryCommandSupported(\"copy\") === true &&\n\t\tdocument.hasFocus()\n\t);\n}\n\nif (!clipboardAvailable()) {\n\tshowManualCopyHint(value); // skip the doomed copy attempt\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait copyText(value);\n} catch (err) {\n\t// show the value in a selectable field instead of failing silently\n\tsetDetailError(\"Copy failed — select the text and press Ctrl/Cmd+C.\");\n}","preventionTips":["Serve over HTTPS or localhost so navigator.clipboard exists","Only call copy from real user gestures with the document focused","Add the clipboard-write allow attribute to embedding iframes","Always provide a selectable-text manual fallback when copy fails"],"tags":["clipboard","browser","fallback"],"backgroundTag":"clipboard-access-denied","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}