neoclide/coc.nvim · error

Extension ${id} not registered

Error message

Extension ${id} not registered

What it means

reloadExtension in src/extension/manager.ts throws `Extension <id> not registered` when there is no registry entry for the id, or when the entry is an Internal extension. Internal (built-in) extensions cannot be reloaded from disk, and an unknown id has nothing to reload, so both cases are rejected up front.

Source

Thrown at src/extension/manager.ts:349

  }

  /**
   * Deactivate & unregist extension
   */
  public async unloadExtension(id: string): Promise<void> {
    let item = this.extensions.get(id)
    if (item) {
      await this.deactivate(id)
      this.extensions.delete(id)
      this._onDidUnloadExtension.fire(id)
    }
    disposeExtension(id)
  }

  public async reloadExtension(id: string): Promise<void> {
    let item = this.extensions.get(id)
    if (!item || item.type == ExtensionType.Internal) {
      throw new Error(`Extension ${id} not registered`)
    }
    if (item.type == ExtensionType.SingleFile) {
      await this.loadExtensionFile(item.filepath)
    } else {
      await this.loadExtension(item.directory)
    }
  }

  public async call(id: string, method: string, args: any[]): Promise<any> {
    let item = this.extensions.get(id)
    if (!item) throw new Error(`extension ${id} not registered`)
    let { extension } = item
    if (!extension.isActive) {
      await this.activate(id)
    }
    let { exports } = extension
    if (!exports || typeof exports[method] !== 'function') {
      throw new Error(`method ${method} not found on extension ${id}`)

View on GitHub (pinned to 50e974d969)

Solutions

  1. Confirm the id is registered (getExtension(id) !== undefined) and not internal before reloading
  2. If the extension was unloaded intentionally, ignore or dispose the stale watcher instead of reloading
  3. Reload via the correct path: loadExtension(directory) for folder extensions or loadExtensionFile for single-file ones
  4. For internal extensions, restart the coc server instead of hot-reloading

Example fix

// before
await manager.reloadExtension(id) // id may be stale/internal
// after
const item = manager.getExtension(id)
if (item && item.type !== ExtensionType.Internal) await manager.reloadExtension(id)
Defensive patterns

Strategy: validation

Validate before calling

const item = manager.getExtension(id)
if (!item || item.type === 'Internal') return // cannot reload

Try / catch

try {
  await manager.reloadExtension(id)
} catch (e) {
  if (/not registered$/.test(e.message)) {
    logger.warn(`cannot reload ${id}: not reloadable`)
  } else throw e
}

Prevention

When it happens

Trigger: watchExtension's file/watchman callback firing reloadExtension(id) after the extension was unregistered or unloaded; calling reloadExtension directly with an unknown id or with an internal extension's id.

Common situations: Editing a watched extension file after the extension was disabled/removed, so the watcher fires on a stale id; attempting to hot-reload a built-in internal extension; typo in the id.

Related errors


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