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

  1. Serve the app over HTTPS or from localhost so the async Clipboard API is available
  2. Trigger copy from a genuine user gesture (onClick handler) and ensure document.hasFocus()
  3. Add the clipboard-write allow attribute to embedding iframes
  4. 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

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.