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
- Serve the app over HTTPS or localhost so navigator.clipboard.writeText is available
- Ensure the copy runs inside the click handler (user activation) rather than async after await
- Fall back to showing the log content in a selectable modal so the user can copy manually
- 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
- Prefer navigator.clipboard.writeText with execCommand only as last resort
- Call copy synchronously inside the click handler to keep user activation
- Serve the app over HTTPS so the Clipboard API is available
- Remove the temp textarea in a finally block
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.