{"record":{"id":"3fcdd052917b7527","repo":"stablyai/orca","slug":"cannot-download-a-directory","errorCode":null,"errorMessage":"Cannot download a directory","messagePattern":"Cannot download a directory","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/filesystem.ts","lineNumber":647,"sourceCode":"        return { content: '', isBinary: true }\n      }\n\n      return { content: buffer.toString('utf-8'), isBinary: false }\n    }\n  )\n\n  ipcMain.handle(\n    'fs:downloadFile',\n    async (\n      event,\n      args: { filePath?: string; connectionId?: string }\n    ): Promise<DownloadFileResult> => {\n      const filePath = validateRequiredString(args?.filePath, 'filePath')\n      const connectionId = validateRequiredString(args?.connectionId, 'connectionId')\n      const provider = requireSshFilesystemProvider(connectionId)\n      const remoteStat = await provider.stat(filePath)\n      if (remoteStat.type === 'directory') {\n        throw new Error('Cannot download a directory')\n      }\n      if (!provider.downloadFile) {\n        throw new Error('Remote file download is unavailable. Reconnect the SSH target and retry.')\n      }\n\n      const remoteBasename = getRuntimePathBasename(filePath)\n      const defaultPath = sanitizeLocalDownloadFilename(remoteBasename)\n      const parentWindow = BrowserWindow.fromWebContents(event.sender) ?? undefined\n      const dialogResult = parentWindow\n        ? await dialog.showSaveDialog(parentWindow, { defaultPath })\n        : await dialog.showSaveDialog({ defaultPath })\n      if (dialogResult.canceled || !dialogResult.filePath) {\n        return { canceled: true }\n      }\n\n      const destinationPath = dialogResult.filePath\n      const { existed } = await inspectDownloadDestination(destinationPath)\n      const tempPath = createSiblingTransferPath(destinationPath, 'download')","sourceCodeStart":629,"sourceCodeEnd":665,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/filesystem.ts#L629-L665","documentation":"Thrown by the fs:downloadFile IPC handler when the SSH remote path resolves to a directory. The handler calls provider.stat on the remote path; if remoteStat.type is 'directory', the download is refused because the handler downloads individual files, not directory trees.","triggerScenarios":"Calling fs:downloadFile with a connectionId and filePath that points to a directory on the remote SSH host. The provider.stat call returns type 'directory' before the save dialog is shown.","commonSituations":"The user selects a directory entry in the remote file browser and triggers download. A path stored in state or clipboard is a directory rather than a file. The remote filesystem layout changed so a previously-file path is now a directory.","solutions":["Select a file (not a directory) in the remote file browser before triggering download.","If you need the entire directory, use scp/rsync from a terminal to recursively download it.","In the renderer, check the entry type before enabling the download action."],"exampleFix":"// before: download remote path /home/user/project (a directory)\n// after: download a specific file\n//   /home/user/project/README.md\n//\n// or use scp for directories:\n//   scp -r user@host:/home/user/project ./local-project","handlingStrategy":"type-guard","validationCode":"// Before calling fs:downloadFile, check the remote entry type\nasync function assertRemoteIsFile(provider: SshFilesystemProvider, filePath: string): Promise<void> {\n  const st = await provider.stat(filePath)\n  if (st.type === 'directory') {\n    throw new Error(`${filePath} is a directory — select a file to download`)\n  }\n}\n\n// Or in the renderer, check the entry metadata:\n// if (selectedEntry.type === 'directory') {\n//   showUserError('Select a file, not a directory')\n//   return\n// }","typeGuard":"function isRemoteFileEntry(entry: { type: string }): boolean {\n  return entry.type === 'file'\n}\n\n// Usage in renderer:\n// if (!isRemoteFileEntry(selectedEntry)) {\n//   showUserError('Directories cannot be downloaded. Select a file.')\n//   return\n// }","tryCatchPattern":"try {\n  await ipcRenderer.invoke('fs:downloadFile', { filePath, connectionId })\n} catch (error) {\n  if (error instanceof Error && error.message === 'Cannot download a directory') {\n    showUserError('Select a file to download. Use scp -r for directories.')\n    return\n  }\n  throw error\n}","preventionTips":["Check the remote entry type in the file browser before enabling the download action.","Disable or hide the download button for directory entries.","For directory downloads, instruct users to use scp/rsync from a terminal."],"tags":["download","ssh","remote","filesystem","validation","ipc"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}