{"record":{"id":"c27c1de4d1c72272","repo":"BloopAI/vibe-kanban","slug":"failed-to-copy-repository-path","errorCode":null,"errorMessage":"Failed to copy repository path","messagePattern":"Failed to copy repository path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/web-core/src/shared/actions/index.ts","lineNumber":1111,"sourceCode":"    },\n  },\n\n  // === Repo-specific Actions (for command bar when selecting a repo) ===\n  RepoCopyPath: {\n    id: 'repo-copy-path',\n    label: 'Copy Repo Path',\n    icon: CopyIcon,\n    requiresTarget: ActionTargetType.GIT,\n    isVisible: (ctx) => ctx.hasWorkspace && ctx.hasGitRepos,\n    execute: async (_ctx, _workspaceId, repoId) => {\n      try {\n        const repo = await repoApi.getById(repoId);\n        if (repo?.path) {\n          await navigator.clipboard.writeText(repo.path);\n        }\n      } catch (err) {\n        console.error('Failed to copy repo path:', err);\n        throw new Error('Failed to copy repository path');\n      }\n    },\n  },\n\n  RepoOpenInIDE: {\n    id: 'repo-open-in-ide',\n    label: 'Open Repo in IDE',\n    icon: DesktopIcon,\n    requiresTarget: ActionTargetType.GIT,\n    isVisible: (ctx) => ctx.hasWorkspace && ctx.hasGitRepos,\n    execute: async (_ctx, _workspaceId, repoId) => {\n      try {\n        const response = await repoApi.openEditor(repoId, {\n          editor_type: null,\n          file_path: null,\n        });\n        if (response.url) {\n          window.open(response.url, '_blank');","sourceCodeStart":1093,"sourceCodeEnd":1129,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/packages/web-core/src/shared/actions/index.ts#L1093-L1129","documentation":"This error is thrown by the RepoCopyPath workspace action after navigator.clipboard.writeText(repo.path) fails. The Clipboard API is async and rejects when the document is not focused, permissions are denied, or the browser context is insecure (non-HTTPS/localhost). The action catches the low-level error, logs it, and rethrows a generic Error so callers see a consistent message.","triggerScenarios":"Executing the RepoCopyPath action when repoApi.getById(repoId) returns a repo with a path, but navigator.clipboard.writeText rejects — e.g. document lacks focus, clipboard-write permission denied via Permissions API, iframe without allow=\"clipboard-write\", or page served over plain HTTP.","commonSituations":"Clicking the copy-path action in a window that lost focus (async delay between click and write), running the local web app over http:// on a LAN IP instead of localhost, embedding the app in an iframe, or browsers blocking clipboard access due to user-denied permission prompts.","solutions":["Retry the copy synchronously inside the user-gesture handler so the document is focused when writeText runs.","Serve the app over HTTPS or localhost — navigator.clipboard is undefined on insecure origins.","Check navigator.permissions.query({name:'clipboard-write'}) and prompt the user to grant clipboard access.","Add a fallback using document.execCommand('copy') with a temporary textarea when the async API is unavailable.","If repo.path is empty/stale, refresh the repository record via repoApi.getById before copying."],"exampleFix":"// before\nawait navigator.clipboard.writeText(repo.path);\n// after\nif (navigator.clipboard && document.hasFocus()) {\n  await navigator.clipboard.writeText(repo.path);\n} else {\n  const ta = document.createElement('textarea');\n  ta.value = repo.path;\n  document.body.appendChild(ta);\n  ta.select();\n  document.execCommand('copy');\n  ta.remove();\n}","handlingStrategy":"fallback","validationCode":"// before copying\nif (!navigator.clipboard) throw new Error('Clipboard API unavailable (insecure context)');\nif (!repo?.path) throw new Error('Repository has no path to copy');\nconst perm = await navigator.permissions?.query({ name: 'clipboard-write' });\nif (perm && perm.state === 'denied') throw new Error('Clipboard write permission denied');","typeGuard":"function canUseClipboard(win: Window): win is Window & { navigator: Navigator & { clipboard: Clipboard } } {\n  return 'clipboard' in win.navigator && win.isSecureContext;\n}","tryCatchPattern":"try {\n  await navigator.clipboard.writeText(repo.path);\n} catch (err) {\n  console.error('Failed to copy repo path:', err);\n  fallbackCopyViaExecCommand(repo.path); // textarea + execCommand('copy')\n}","preventionTips":["Only invoke clipboard writes directly inside a user-gesture handler.","Serve the app over HTTPS or localhost (secure context).","Add allow=\"clipboard-write\" when embedding the app in an iframe.","Keep an execCommand('copy') fallback for unsupported browsers."],"tags":["browser","clipboard","permissions"],"backgroundTag":"clipboard-write-permission-denied","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}