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

  1. Ensure copyToClipboard is invoked synchronously from a real user event (click handler) so execCommand is allowed.
  2. Serve the app over HTTPS (or localhost) so the modern navigator.clipboard API is available and the fallback is rarely needed.
  3. Check document.hasFocus() before copying and defer the call (e.g. after focusing the window or awaiting user interaction).
  4. Catch the error and show an instructive snackbar telling the user to copy manually (select + Ctrl/Cmd-C).
  5. 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

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)