neoclide/coc.nvim · error · Error

Unable to load extension at ${extensionRoot}, missing packag

Error message

Unable to load extension at ${extensionRoot}, missing package.json

What it means

Thrown by ExtensionManager.load() when loading a source-code extension whose root folder has no readable package.json, or the parsed JSON has no `name` field. coc.nvim requires a package.json manifest to identify and register an extension. It indicates the given extensionRoot does not contain a valid extension.

Source

Thrown at src/extension/manager.ts:655

      void window.showInformationMessage(`watching ${item.directory}`)
      client.subscribe('**/*.js', async () => {
        this.reloadExtension(id).then(() => {
          void window.showInformationMessage(`reloaded ${id}`)
        }, onUnexpectedError)
      })
    }
  }

  /**
   * load extension in folder or file
   */
  public async load(filepath: string, active: boolean, options?: ExtensionLoadOptions): Promise<ExportExtension> {
    let name: string
    if (options?.sourceCode) {
      let extensionRoot = options.extensionRoot ?? filepath
      let obj = loadJson(path.join(extensionRoot, 'package.json')) as any
      name = obj.name
      if (!name) throw new Error(`Unable to load extension at ${extensionRoot}, missing package.json`)
      await this.unloadExtension(name)
      await this.registerExtension(extensionRoot, obj, ExtensionType.Local, true, options)
    } else if (isDirectory(filepath)) {
      let obj = loadJson(path.join(filepath, 'package.json')) as any
      name = obj.name
      await this.loadExtension(filepath, true)
    } else {
      name = await this.loadExtensionFile(filepath, true)
    }
    if (!name) throw new Error(`Unable to load extension at ${filepath}`)
    let disabled = this.states.isDisabled(name)
    if (disabled) throw new Error(`extension ${name} is disabled`)
    let item = this.getExtension(name)
    if (active) await this.activate(name)
    return {
      get isActive() {
        return item.extension.isActive
      },

View on GitHub (pinned to 50e974d969)

Solutions

  1. Ensure extensionRoot points to the directory containing a valid package.json with a `name` field
  2. Verify package.json parses as valid JSON (`cat package.json | node -e 'JSON.parse(require("fs").readFileSync(0))'`)
  3. Add or fix the `name` field in package.json
  4. Check the path exists and is the extension root, not a subfolder

Example fix

// before
await coc.extensions.load('/path/to/ext/index.js', true, { sourceCode: true })
// after
await coc.extensions.load('/path/to/ext', true, { sourceCode: true }) // dir containing package.json
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
const root = options?.extensionRoot ?? filepath;
const pkgPath = require('path').join(root, 'package.json');
if (!fs.existsSync(pkgPath)) throw new Error(`No package.json at ${root}`);
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
if (!pkg.name) throw new Error(`package.json at ${root} has no name`);

Try / catch

try {
  await coc.extensions.load(filepath, true, { sourceCode: true });
} catch (e) {
  if (String(e.message).includes('missing package.json')) {
    // fall back or prompt user to fix extension root
  } else throw e;
}

Prevention

When it happens

Trigger: Calling coc.extensions.load(filepath, active, {sourceCode: true}) where options.extensionRoot (default filepath) is a directory without package.json, or the package.json lacks a `name` field, or JSON.parse fails silently returning an object without name.

Common situations: Pointing load() at the source file instead of the project root; loading an extension folder that was never built/cloned fully; a malformed package.json; typo'd extensionRoot path.

Related errors


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/7b6f5fe68afeaa35. Report an issue: GitHub.