semaphoreui/semaphore · warning
Fallback copy failed
Error message
Fallback copy failed
What it means
copyToClipboard first tries the async Clipboard API and falls back to a hidden-textarea + document.execCommand('copy') trick for older browsers. When execCommand returns false (the browser refused or could not perform the copy), the function throws 'Fallback copy failed' instead of emitting the success snackbar. It signals that no clipboard mechanism succeeded in the current browser context.
Solutions
- Ensure copyToClipboard is invoked synchronously from a real user event (click handler) so execCommand is allowed.
- Serve the app over HTTPS (or localhost) so the modern navigator.clipboard API is available and the fallback is rarely needed.
- Check document.hasFocus() before copying and defer the call (e.g. after focusing the window or awaiting user interaction).
- Catch the error and show an instructive snackbar telling the user to copy manually (select + Ctrl/Cmd-C).
- Avoid calling it from inside restricted iframes, or add clipboard permissions to the iframe sandbox.
Example fix
// before
this.copyResult = result; // copy happens outside a user gesture
// after
button.addEventListener('click', async () => {
try {
await copyToClipboard(result, 'Copied!');
} catch (e) {
EventBus.$emit('i-snackbar', { color: 'error', text: 'Copy failed — please copy manually' });
}
}); Defensive patterns
Strategy: try-catch
Validate before calling
if (!document.hasFocus() || (!navigator.clipboard && !document.queryCommandSupported('copy'))) {
showManualCopyHint();
return;
} Type guard
function canCopyToClipboard() {
return typeof navigator !== 'undefined' &&
(navigator.clipboard != null || document.queryCommandSupported?.('copy') === true);
} Try / catch
try {
await copyToClipboard(text, 'Copied!');
} catch (e) {
if (e.message === 'Fallback copy failed') {
EventBus.$emit('i-snackbar', { color: 'error', text: 'Copy blocked — please copy manually' });
selectTextForManualCopy(text);
} else {
throw e;
}
} Prevention
- Only trigger clipboard writes from direct user gestures (click/keydown handlers).
- Serve the app on HTTPS so navigator.clipboard is available.
- Guard with document.hasFocus() and clipboard feature detection before copying.
- Always wrap clipboard calls in try/catch and offer a manual copy UI as fallback.
- Avoid embedded iframe contexts without clipboard permissions.
When it happens
Trigger: Calling copyToClipboard in a browser where the Clipboard API is unavailable (non-secure context, older browser) AND document.execCommand('copy') returns false — e.g. the call is not triggered by a user gesture, the page lacks focus, or an iframe lacks the clipboard permission.
Common situations: Programmatic/automatic copy attempts without a click; page copied from inside a sandboxed iframe without allow-same-origin/clipboard permissions; Safari or older mobile browsers; running the page over plain HTTP so navigator.clipboard is undefined; browser window blurred when the copy runs.
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/1c7eed3617e4e6f4.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/lib/copyToClipboard.js:25
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
const selected = document.getSelection().rangeCount > 0
? document.getSelection().getRangeAt(0) : false;
el.select();
document.execCommand('copy');
document.body.removeChild(el);
if (selected) {
document.getSelection().removeAllRanges();
document.getSelection().addRange(selected);
}
const successful = document.execCommand('copy');
// document.body.removeChild(textArea);
if (!successful) {
throw new Error('Fallback copy failed');
}
EventBus.$emit('i-snackbar', {
color: 'success',
text: message,
});
} catch (e) {
EventBus.$emit('i-snackbar', {
color: 'error',
text: `Can't copy to clipboard: ${e.message}`,
});
}
}
View on GitHub (pinned to 1774ccb71a)