neoclide/coc.nvim · error
Invalid dest path: ${dest}
Error message
Invalid dest path: ${dest} What it means
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>'.
Source
Thrown at src/model/download.ts:221
/**
* Download file from url, with optional untar/unzip support.
* @param {string} url
* @param {DownloadOptions} options contains dest folder and optional onProgress callback
*/
export default function download(urlInput: string | URL, options: DownloadOptions, token?: CancellationToken, obj: any = {}): Promise<string> {
let url = toURL(urlInput)
let { etagAlgorithm } = options
let { dest, onProgress, extract } = options
for (let [name, value] of Object.entries({
maxDownloadSize: options.maxDownloadSize,
maxExtractSize: options.maxExtractSize,
maxArchiveEntries: options.maxArchiveEntries
})) {
if (value !== undefined && (!Number.isFinite(value) || value <= 0)) throw new Error(`${name} must be a positive finite number`)
}
if (!dest || !path.isAbsolute(dest)) {
throw new Error(`Invalid dest path: ${dest}`)
}
// Use one canonical lexical representation for filesystem operations and
// archive-boundary checks. path.resolve also removes a trailing separator.
dest = path.resolve(dest)
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true })
} else {
let stat = fs.statSync(dest)
if (stat && !stat.isDirectory()) {
throw new Error(`${dest} exists, but not directory!`)
}
}
let mod = getRequestModule(url)
let opts = resolveRequestOptions(url, options)
if (!opts.agent && options.agent) opts.agent = options.agent
let extname = path.extname(url.pathname)
return new Promise<string>((resolve, reject) => {
let timer: NodeJS.TimeoutView on GitHub (pinned to 50e974d969)
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(...).
Example fix
// before
download({ url, dest: '~/downloads/pkg.zip' })
// after
import os from 'os'
download({ url, dest: path.join(os.homedir(), 'downloads', 'pkg.zip') }) Defensive patterns
Strategy: validation
Validate before calling
const path = require('path'), os = require('os')
function assertAbsDir(p) {
if (!p || !path.isAbsolute(p)) throw new Error(`dest must be an absolute path, got: ${p}`)
return p
}
const dest = assertAbsDir(path.join(os.homedir(), 'downloads')) Type guard
function isAbsolutePath(p: unknown): p is string { return typeof p === 'string' && p.length > 0 && path.isAbsolute(p) } Try / catch
try {
await download({ url, dest })
} catch (e) {
if (/Invalid dest path/.test(e.message)) {
return download({ url, dest: path.resolve(dest || '.') })
}
throw e
} Prevention
- 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.
When it happens
Trigger: Calling download({ url }) without dest; passing a relative path like 'downloads/pkg.zip' or '~/pkg.zip'; passing dest: '' from an unset config value.
Common situations: 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.
Related errors
- ${dest} exists, but not directory!
- ${name} must be a positive finite number
- Directory ${folder} not exists
- name and doComplete required for createSource
- Feature param could only starts with nvim and patch
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/64fce1bd840ed9ac.
Report an issue: GitHub.