{"record":{"id":"64fce1bd840ed9ac","repo":"neoclide/coc.nvim","slug":"invalid-dest-path-dest","errorCode":null,"errorMessage":"Invalid dest path: ${dest}","messagePattern":"Invalid dest path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/model/download.ts","lineNumber":221,"sourceCode":"\n/**\n * Download file from url, with optional untar/unzip support.\n * @param {string} url\n * @param {DownloadOptions} options contains dest folder and optional onProgress callback\n */\nexport default function download(urlInput: string | URL, options: DownloadOptions, token?: CancellationToken, obj: any = {}): Promise<string> {\n  let url = toURL(urlInput)\n  let { etagAlgorithm } = options\n  let { dest, onProgress, extract } = options\n  for (let [name, value] of Object.entries({\n    maxDownloadSize: options.maxDownloadSize,\n    maxExtractSize: options.maxExtractSize,\n    maxArchiveEntries: options.maxArchiveEntries\n  })) {\n    if (value !== undefined && (!Number.isFinite(value) || value <= 0)) throw new Error(`${name} must be a positive finite number`)\n  }\n  if (!dest || !path.isAbsolute(dest)) {\n    throw new Error(`Invalid dest path: ${dest}`)\n  }\n  // Use one canonical lexical representation for filesystem operations and\n  // archive-boundary checks. path.resolve also removes a trailing separator.\n  dest = path.resolve(dest)\n  if (!fs.existsSync(dest)) {\n    fs.mkdirSync(dest, { recursive: true })\n  } else {\n    let stat = fs.statSync(dest)\n    if (stat && !stat.isDirectory()) {\n      throw new Error(`${dest} exists, but not directory!`)\n    }\n  }\n  let mod = getRequestModule(url)\n  let opts = resolveRequestOptions(url, options)\n  if (!opts.agent && options.agent) opts.agent = options.agent\n  let extname = path.extname(url.pathname)\n  return new Promise<string>((resolve, reject) => {\n    let timer: NodeJS.Timeout","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/model/download.ts#L203-L239","documentation":"download() requires the dest option to be a non-empty absolute filesystem path, because the resolved destination is used directly for fs operations and archive-boundary security checks. An empty string, undefined, or a relative path throws 'Invalid dest path: <value>'.","triggerScenarios":"Calling download({ url }) without dest; passing a relative path like 'downloads/pkg.zip' or '~/pkg.zip'; passing dest: '' from an unset config value.","commonSituations":"Using '~' expecting shell-style expansion (Node does not expand it); building dest by string concatenation where an earlier segment was empty; running from different working directories so a previously-working relative path is now relative to the wrong cwd.","solutions":["Pass an absolute path: path.join(os.homedir(), 'downloads', 'pkg.zip') or path.resolve('downloads').","Replace '~' manually via os.homedir() since Node does not expand it.","Validate before calling: if (!dest || !path.isAbsolute(dest)) throw new Error(...)."],"exampleFix":"// before\ndownload({ url, dest: '~/downloads/pkg.zip' })\n// after\nimport os from 'os'\ndownload({ url, dest: path.join(os.homedir(), 'downloads', 'pkg.zip') })","handlingStrategy":"validation","validationCode":"const path = require('path'), os = require('os')\nfunction assertAbsDir(p) {\n  if (!p || !path.isAbsolute(p)) throw new Error(`dest must be an absolute path, got: ${p}`)\n  return p\n}\nconst dest = assertAbsDir(path.join(os.homedir(), 'downloads'))","typeGuard":"function isAbsolutePath(p: unknown): p is string { return typeof p === 'string' && p.length > 0 && path.isAbsolute(p) }","tryCatchPattern":"try {\n  await download({ url, dest })\n} catch (e) {\n  if (/Invalid dest path/.test(e.message)) {\n    return download({ url, dest: path.resolve(dest || '.') })\n  }\n  throw e\n}","preventionTips":["Always build dest with path.resolve()/path.join() so it is absolute.","Replace '~' with os.homedir() — Node does not expand it.","Assert path.isAbsolute(dest) at config-load time, not at download time."],"tags":["validation","filesystem","download","path"],"backgroundTag":"invalid-destination-path","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}