{"record":{"id":"37848386280eb42d","repo":"stablyai/orca","slug":"clone-destination-must-be-an-absolute-path","errorCode":null,"errorMessage":"Clone destination must be an absolute path","messagePattern":"Clone destination must be an absolute path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/git/repo-clone-path.ts","lineNumber":38,"sourceCode":"  const source = url.replace(/\\.git\\/?$/, '')\n  const isWindowsLocalSource = /^[A-Za-z]:[\\\\/]/.test(source) || source.startsWith('\\\\\\\\')\n  const repoName = isWindowsLocalSource ? win32.basename(source) : posix.basename(source)\n  if (!repoName || repoName === '.' || repoName === '..') {\n    throw new Error('Invalid repository name derived from URL')\n  }\n  if (repoName.includes('/') || repoName.includes('\\\\')) {\n    throw new Error('Invalid repository name derived from URL')\n  }\n  return repoName\n}\n\nexport function deriveValidatedClonePath(args: { url: string; destination: string }): string {\n  if (\n    !args.destination ||\n    !isAbsolute(args.destination) ||\n    (process.platform !== 'win32' && isWindowsAbsolutePathLike(args.destination))\n  ) {\n    throw new Error('Clone destination must be an absolute path')\n  }\n\n  const repoName = deriveCloneRepoNameFromUrl(args.url)\n\n  const clonePath = join(args.destination, repoName)\n  const resolvedDestination = resolve(args.destination)\n  const resolvedClonePath = resolve(clonePath)\n  const pathFromDestination = relative(resolvedDestination, resolvedClonePath)\n  if (\n    pathFromDestination === '' ||\n    pathFromDestination === '..' ||\n    pathFromDestination.startsWith(`..${sep}`) ||\n    isAbsolute(pathFromDestination)\n  ) {\n    throw new Error('Clone path must be inside the destination directory')\n  }\n\n  return clonePath","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/git/repo-clone-path.ts#L20-L56","documentation":"deriveValidatedClonePath throws when args.destination is empty, is not an absolute path, or — on a non-Windows host — looks like a Windows absolute path (a Linux/macOS host must not accept a Windows drive path as the clone destination). This is the entry guard before the repo name is even derived; it refuses to proceed with a destination that cannot be safely joined.","triggerScenarios":"Calling deriveValidatedClonePath({ url, destination }) where destination is '', a relative path like 'repos/foo', undefined, or (on Linux/macOS) a Windows-style path like 'C:\\Users\\me\\repos' or a UNC '\\\\server\\share' that the host OS cannot interpret correctly.","commonSituations":"A clone form that did not absolutize a user-typed relative path; cross-platform UI bug where a Windows path captured on one host is replayed on another; a destination read from config that was stored as a relative path; an empty form field not caught by the UI before reaching the main process.","solutions":["Resolve the destination against a known base directory (path.resolve) before calling, so it is always absolute.","On non-Windows hosts, reject Windows-style paths at the UI layer and ask for a POSIX path.","Validate the destination is non-empty in the form before submitting the clone request.","If the destination comes from config, store and re-read it as an absolute path."],"exampleFix":"// before\nderiveValidatedClonePath({ url, destination: userTypedDest })\n\n// after: absolutize on the host, reject cross-platform shape mismatch\nimport { resolve, isAbsolute } from 'node:path'\nif (!userTypedDest || !isAbsolute(resolve(userTypedDest))) {\n  throw new Error('Pick an absolute clone destination folder.')\n}\nderiveValidatedClonePath({ url, destination: resolve(userTypedDest) })","handlingStrategy":"validation","validationCode":"import { isAbsolute, resolve } from 'node:path'\nimport { isWindowsAbsolutePathLike } from '../../shared/cross-platform-path'\n\nfunction isValidCloneDestination(destination: string): boolean {\n  return Boolean(destination) && isAbsolute(resolve(destination))\n    && (process.platform === 'win32' || !isWindowsAbsolutePathLike(destination))\n}\n\nif (!isValidCloneDestination(destination)) throw new Error('Clone destination must be an absolute path on this host.')","typeGuard":"function isCloneDestinationNotAbsolute(error: unknown): boolean {\n  return error instanceof Error && error.message === 'Clone destination must be an absolute path'\n}","tryCatchPattern":"if (!isValidCloneDestination(destination)) {\n  showFormError('destination', 'Pick an absolute folder on this host.')\n  return\n}\ntry { deriveValidatedClonePath({ url, destination }) }\ncatch (error) { if (isCloneDestinationNotAbsolute(error)) { showFormError('destination', 'Destination must be absolute.'); return } throw error }","preventionTips":["Always path.resolve the destination against a known base before calling.","On non-Windows hosts, reject Windows-style destinations at the UI layer.","Store destination paths as absolute in config; never read and replay relative paths."],"tags":["git","clone","validation","path","cross-platform"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}