flipped-aurora/gin-vue-admin · warning · Error

copy failed

Error message

copy failed

What it means

The log viewer's copy function falls back to the deprecated document.execCommand('copy') when the Clipboard API is unavailable. execCommand returns false when the browser refuses the copy, so the code throws 'copy failed' and shows ElMessage error '复制失败'.

Source

Thrown at web/src/view/systemTools/logViewer/index.vue:602

  if (path !== activePath.value) return
  const nextPath = openedPaths.value[index] || openedPaths.value[index - 1] || ''
  resetContentState()
  if (nextPath) openFile(nextPath)
}

async function copyContent() {
  if (!content.value) return
  try {
    if (navigator.clipboard?.writeText) {
      await navigator.clipboard.writeText(content.value)
    } else {
      const textarea = document.createElement('textarea')
      textarea.value = content.value
      textarea.setAttribute('readonly', '')
      textarea.className = 'fixed left-[-9999px] top-0'
      document.body.appendChild(textarea)
      textarea.select()
      if (!document.execCommand('copy')) throw new Error('copy failed')
      document.body.removeChild(textarea)
    }
    ElMessage.success('日志内容已复制')
  } catch {
    ElMessage.error('复制失败')
  }
}

const updateRootHeight = () => {
  const element = rootRef.value
  if (!element) return
  const top = element.getBoundingClientRect().top
  rootHeight.value = `${Math.max(320, window.innerHeight - top - 32)}px`
}

onMounted(() => {
  nextTick(updateRootHeight)
  window.addEventListener('resize', updateRootHeight)

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Serve the app over HTTPS or localhost so navigator.clipboard.writeText is available
  2. Ensure the copy runs inside the click handler (user activation) rather than async after await
  3. Fall back to showing the log content in a selectable modal so the user can copy manually
  4. Handle the textarea removal in a finally block so the hidden node is never leaked

Example fix

// before
textarea.select()
if (!document.execCommand('copy')) throw new Error('copy failed')
document.body.removeChild(textarea)
// after
textarea.select()
let ok = false
try {
  ok = document.execCommand('copy')
} finally {
  document.body.removeChild(textarea)
}
if (!ok) throw new Error('copy failed')
Defensive patterns

Strategy: fallback

Validate before calling

const canUseClipboard = navigator.clipboard && window.isSecureContext
if (!canUseClipboard) console.warn('Clipboard API unavailable, execCommand fallback will be used')

Type guard

function supportsClipboardAPI() {
  return Boolean(navigator.clipboard && typeof navigator.clipboard.writeText === 'function')
}

Try / catch

try {
  await copyLog(content.value)
  ElMessage.success('日志内容已复制')
} catch {
  ElMessage.error('复制失败,请手动选择文本复制')
}

Prevention

When it happens

Trigger: navigator.clipboard is unavailable (non-secure context) AND document.execCommand('copy') returns false, e.g. the synthetic textarea is not selectable or copy is blocked.

Common situations: Page served over plain HTTP (clipboard API requires HTTPS/localhost); browser blocks programmatic copy without user activation; iframe with restrictive permissions policy.


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/9d989b7f94a1ac5b. Report an issue: GitHub.