neoclide/coc.nvim · error · Error
main file ${main} not found, you may need to build the proje
Error message
main file ${main} not found, you may need to build the project. What it means
Thrown by loadGlobalJsonAsync when the extension has no engines.vscode entry and its declared main entry file does not exist on disk. This usually means the extension source has not been compiled/built.
Source
Thrown at src/extension/stat.ts:285
function getEntryFile(main: string | undefined): string {
if (!main) return 'index.js'
if (!main.endsWith('.js')) return main + '.js'
return main
}
export async function loadGlobalJsonAsync(folder: string, version: string): Promise<ExtensionJson> {
let jsonFile = path.join(folder, 'package.json')
let content = await readFile(jsonFile, 'utf8')
let packageJSON = JSON.parse(content) as ExtensionJson
let { engines } = packageJSON
let main = getEntryFile(packageJSON.main)
if (!engines || (typeof engines.coc !== 'string' && typeof engines.vscode !== 'string')) throw new Error('Invalid engines field')
let keys = Object.keys(engines)
if (keys.includes('coc') && !semver.satisfies(version, engines['coc'].replace(/^\^/, '>='))) {
throw new Error(`coc.nvim version not match, required ${engines['coc']}`)
}
if (!engines.vscode && !fs.existsSync(path.join(folder, main))) {
throw new Error(`main file ${main} not found, you may need to build the project.`)
}
return packageJSON
}
export function loadExtensionJson(folder: string, version: string, errors: string[]): ExtensionJson | undefined {
let jsonFile = path.join(folder, 'package.json')
if (!fs.existsSync(jsonFile)) {
errors.push(`package.json not found in ${folder}`)
return undefined
}
let packageJSON = loadJson(jsonFile) as ExtensionJson
let { name, engines } = packageJSON
let main = getEntryFile(packageJSON.main)
if (!name) errors.push(`can't find name in package.json`)
if (!engines || !objectLiteral(engines)) {
errors.push(`invalid engines in ${jsonFile}`)
}
if (engines && !engines.vscode && !fs.existsSync(path.join(folder, main))) {View on GitHub (pinned to 50e974d969)
Solutions
- Build the extension (npm install && npm run build / yarn build) to produce the main file
- Verify the `main` field in package.json matches the actual output file
- Reinstall the extension from a published release
Example fix
// before ls my-ext # only src/, no lib/ // after cd my-ext && npm install && npm run build # creates lib/index.js referenced by main
Defensive patterns
Strategy: validation
Validate before calling
const pkg = JSON.parse(fs.readFileSync(path.join(folder, 'package.json'), 'utf8'));
if (!pkg.engines?.vscode) {
const main = pkg.main || 'index.js';
if (!fs.existsSync(path.join(folder, main))) {
console.error(`main file ${main} missing - build required`);
}
} Try / catch
try {
await coc.extensions.load(folder, true);
} catch (e) {
if (String(e.message).includes('main file') && String(e.message).includes('not found')) {
console.error(`Build the extension in ${folder} (npm install && npm run build)`);
} else throw e;
} Prevention
- Run npm install && npm run build after cloning from source
- Verify main file exists before packaging/loading
- Use published releases for production setups
When it happens
Trigger: Loading an extension folder where package.json main (e.g. lib/index.js) is missing because the project was never built, was partially installed, or main field is wrong.
Common situations: Cloning an extension from source and loading it without running npm install/build; missing dist/lib after CI packaging; TypeScript extension without compiled output.
Related errors
- Unable to load extension at ${extensionRoot}, missing packag
- Unable to load extension at ${filepath}
- extension ${name} is disabled
- Invalid engines field
- coc.nvim version not match, required ${engines['coc']}
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/661b8d6cfa19cfa8.
Report an issue: GitHub.