moeru-ai/airi · warning

Clipboard API is unavailable

Error message

Clipboard API is unavailable

What it means

stage-render-error.vue builds a bug report and copies it to the clipboard via VueUse's useClipboard({ legacy: true }). isClipboardSupported is false when neither navigator.clipboard (async Clipboard API) nor document.execCommand('copy') (legacy fallback) is available. The code checks the flag before copying and throws this error so the UI can show it inside the bug report dialog.

Source

Thrown at packages/stage-ui/src/components/scenes/stage-render-error.vue:45

const bugReportSubmitError = shallowRef<unknown>()

function openBugReportDialog() {
  bugReportDescription.value = t('stage.render-error.report-description', {
    renderer: props.renderer,
    modelId: props.modelId ?? 'unknown',
    error: errorMessageFrom(props.error) ?? props.error.message,
  })
  bugReportSubmitError.value = undefined
  showBugReportDialog.value = true
}

async function submitBugReport(payload: BugReportDialogSubmitPayload) {
  bugReportSending.value = true
  bugReportSubmitError.value = undefined

  try {
    if (!isClipboardSupported.value)
      throw new Error('Clipboard API is unavailable')

    await copy(payload.formattedReport)
    showBugReportDialog.value = false
  }
  catch (error) {
    bugReportSubmitError.value = error
  }
  finally {
    bugReportSending.value = false
  }
}
</script>

<template>
  <div
    :class="[
      'absolute inset-0 z-20 p-6',
      'flex items-center justify-center',

View on GitHub (pinned to 677329427f)

Solutions

  1. Serve the app from a secure context: https:// or http://localhost / http://127.0.0.1.
  2. Grant clipboard-read/clipboard-write permission (Permissions-Policy header, or Electron session setPermissionRequestHandler).
  3. Update the embedded WebView/Chromium so navigator.clipboard exists.
  4. As a product fallback, render the report in a selectable textarea so users can copy manually when isClipboardSupported is false.

Example fix

// before
if (!isClipboardSupported.value)
  throw new Error('Clipboard API is unavailable')
await copy(payload.formattedReport)

// after — degrade gracefully instead of throwing
if (!isClipboardSupported.value) {
  manualCopyText.value = payload.formattedReport
  bugReportSubmitError.value = new Error('Clipboard unavailable — copy the report text below manually.')
  return
}
await copy(payload.formattedReport)
Defensive patterns

Strategy: type-guard

Validate before calling

import { useClipboard } from '@vueuse/core'
const { copy, isSupported } = useClipboard({ legacy: true })
if (!isSupported.value) {
  // render manual-copy fallback (textarea) instead of submitting to copy()
}

Type guard

function isClipboardAvailable(): boolean {
  return typeof navigator !== 'undefined'
    && ((navigator.clipboard != null && window.isSecureContext) || typeof document?.execCommand === 'function')
}

Try / catch

try {
  await copy(payload.formattedReport)
}
catch (error) {
  // Firefox/permission rejections still throw even when isSupported is true
  bugReportSubmitError.value = error
}

Prevention

When it happens

Trigger: Submitting the bug report dialog in a non-secure context (page served over http:// on a non-localhost host), an old embedded WebView without the async clipboard API, or an environment where document.execCommand is also unavailable, making both the modern and legacy paths unsupported.

Common situations: Running stage-web over plain http on a LAN IP or inside a VM/remote preview; older Electron/Chromium WebView with permissions policies blocking clipboard; iframe sandbox without allow-clipboard-write; headless/automated browsers with clipboard disabled.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/f123ed63cfb635ae. Report an issue: GitHub.