gitbutlerapp/gitbutler · warning
Clipboard access is unavailable.
Error message
Clipboard access is unavailable.
What it means
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.
Source
Thrown at packages/but-mcp-app/src/WorkspaceApp.tsx:217
timeStyle: "short",
}).format(date);
}
async function copyText(value: string): Promise<void> {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(value);
return;
}
const input = document.createElement("textarea");
input.value = value;
input.style.position = "fixed";
input.style.opacity = "0";
document.body.appendChild(input);
input.select();
const copied = document.execCommand("copy");
input.remove();
if (!copied) throw new Error("Clipboard access is unavailable.");
}
function GraphSegment({ kind, status }: { kind: "branch" | "commit"; status: GraphStatus }) {
return (
<span className="graph-segment" data-status={status} aria-hidden="true">
<svg viewBox="0 0 16 28" fill="none">
{kind === "commit" ? (
<>
<path className="graph-rail" d="M8 0V11M8 17V28" />
<circle cx="8" cy="14" r="3.5" />
</>
) : (
<>
<path className="graph-rail" d="M8 0V28" />
<path d="M16 14H14C10.6863 14 8 16.6863 8 20" />
</>
)}
</svg>View on GitHub (pinned to 2497b8007a)
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
Example fix
// before
const copied = document.execCommand("copy");
input.remove();
if (!copied) throw new Error("Clipboard access is unavailable.");
// after — degrade to a manual-copy hint instead of failing
const copied = document.execCommand("copy");
input.remove();
if (!copied) {
setDetailError("Automatic copy failed — the value stays selected; press Ctrl/Cmd+C.");
return;
} Defensive patterns
Strategy: validation
Validate before calling
function clipboardAvailable(): boolean {
if (navigator.clipboard && window.isSecureContext) return true;
return (
typeof document.queryCommandSupported === "function" &&
document.queryCommandSupported("copy") === true &&
document.hasFocus()
);
}
if (!clipboardAvailable()) {
showManualCopyHint(value); // skip the doomed copy attempt
} Try / catch
try {
await copyText(value);
} catch (err) {
// show the value in a selectable field instead of failing silently
setDetailError("Copy failed — select the text and press Ctrl/Cmd+C.");
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Developing over a LAN IP with http://; the app embedded in a webview/iframe; automated or headless environments; Safari quirks that require explicit focus.
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/f19ac6e19214ee5a.
Report an issue: GitHub.