neoclide/coc.nvim · error
Invalid extension name: ${name}
Error message
Invalid extension name: ${name} What it means
extensionPath validates npm package names before touching the filesystem: the name must be a single path component or a scoped '@scope/pkg' name, without path separators, NUL bytes, '.'/'..' segments, or traversal. The first regex/NUL check throws for syntactically invalid names to prevent registry-controlled metadata from driving destructive filesystem operations.
Source
Thrown at src/extension/installer.ts:18
'use strict'
import { EventEmitter } from 'events'
import { createLogger } from '../logger'
import download, { DownloadOptions } from '../model/download'
import fetch, { FetchOptions } from '../model/fetch'
import { loadJson } from '../util/fs'
import { child_process, fs, minimatch, os, path, readline, semver } from '../util/node'
import { toText } from '../util/string'
import workspace from '../workspace'
const logger = createLogger('extension-installer')
const local_dependencies = ['coc.nvim', 'esbuild', 'webpack', '@types/node']
function extensionPath(root: string, name: string | undefined): string {
// npm package names contain either one path component, or two for a scoped
// package. Reject path syntax before using registry-controlled metadata in
// destructive filesystem operations.
if (typeof name !== 'string' || !/^(?:@[^/\\]+\/)?[^/\\]+$/.test(name) || name.includes('\0') || name.split('/').some(part => part === '.' || part === '..')) {
throw new Error(`Invalid extension name: ${name}`)
}
let resolvedRoot = path.resolve(root)
let target = path.resolve(resolvedRoot, name)
let relative = path.relative(resolvedRoot, target)
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
throw new Error(`Invalid extension name: ${name}`)
}
return target
}
export interface Info {
'dist.tarball'?: string
'engines.coc'?: string
version?: string
name?: string
}
export type Dependencies = Record<string, string>View on GitHub (pinned to 50e974d969)
Solutions
- Validate/normalize the extension name before calling: single component or '@scope/pkg'.
- Strip version suffixes and URI encoding before passing the name.
- Ensure the name is a non-empty string (guard undefined).
- If the source is a URI, parse it properly to extract just the package name.
Example fix
// before
let info = await installer.getInfo('coc-tsserver/../evil') // throws
// after
let name = 'coc-tsserver' // clean npm name: /^(?:@[^/\\]+\/)?[^/\\]+$/
let info = await installer.getInfo(name) Defensive patterns
Strategy: validation
Validate before calling
function isValidExtName(name: unknown): name is string {
return typeof name === 'string' &&
/^(?:@[^/\\]+\/)?[^/\\]+$/.test(name) &&
!name.includes('\0') &&
!name.split('/').some(p => p === '.' || p === '..')
} Type guard
function isExtensionName(v: unknown): v is string {
return typeof v === 'string' && /^(?:@[^/\\]+\/)?[^/\\]+$/.test(v)
} Try / catch
try {
const p = extensionPath(root, name)
} catch (e) {
if (e.message.startsWith('Invalid extension name')) {
throw new UserInputError(`'${name}' is not a valid npm/coc package name`)
} else throw e
} Prevention
- Strip version specifiers ('pkg@1.2.3') and URI encoding before passing names.
- Never pass filesystem paths or user-typed paths to install/info APIs.
- Validate names with the same npm-name regex before calling.
- Parse extension URIs properly to extract the bare package name.
When it happens
Trigger: Calling extensionPath (directly or via getInfo, getInfoFromUri, install, folder, dest) with name = undefined, an empty string, a name containing '/' or '\' beyond scope syntax, embedded NUL, or '.'/'..' segments.
Common situations: Parsing a malformed coc-extension URI; user typed 'coc-tsserver/' or '@scope/' in an install command; split on '/' producing empty parts; programmatic callers passing unparsed 'pkg@version' strings with slashes.
Related errors
- name and doComplete required for createSource
- Feature param could only starts with nvim and patch
- Invalid key ${name} of registerKeymap
- process.${name}() is not allowed in extension sandbox
- Cannot use process.umask() to change mask (read-only)
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/c0cb41c596567f8c.
Report an issue: GitHub.