neoclide/coc.nvim · error

method ${method} not found on extension ${id}

Error message

method ${method} not found on extension ${id}

What it means

ExtensionManager.call() throws `method <method> not found on extension <id>` when the extension is registered (and activated on demand) but its exports object does not expose a function with the requested method name. The runtime exports contract is checked with typeof exports[method] !== 'function'.

Source

Thrown at src/extension/manager.ts:367

      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}`)
    }
    return await Promise.resolve(exports[method].apply(null, args))
  }

  public registContribution(id: string, packageJSON: any, directory: string, filepath?: string): void {
    let { contributes, activationEvents } = packageJSON
    let { configuration, rootPatterns, commands } = contributes ?? {}
    let definitions: IStringDictionary<IJSONSchema> | undefined
    let props = getProperties(configuration ?? {})
    if (!isEmpty(props)) {
      // /configuration
      let properties = convertProperties(props, ConfigurationScope.WINDOW)
      if (Is.objectLiteral(configuration.definitions)) {
        let prefix = id.replace(/[^\w]/g, '')
        const addPrefix = (obj: object, key: string) => {
          if (key == '$ref') {
            let val = obj[key]
            if (Is.string(val) && val.startsWith('#/definitions/')) {

View on GitHub (pinned to 50e974d969)

Solutions

  1. Check the extension's activate() return value — ensure it returns an object containing the method you call
  2. Align caller and extension versions: the method may have been renamed/removed — consult the extension's API docs
  3. Verify exact method name and casing
  4. If you own the extension, export the method: `return { myMethod: async (...args) => {...} }` from activate()

Example fix

// before (extension side)
exports.activate = async () => { /* returns undefined */ }
// after
exports.activate = async () => ({ run: async () => 'ok' })
Defensive patterns

Strategy: try-catch

Validate before calling

const item = manager.getExtension(id)
if (!item?.isActive) await manager.activate(id)
const exp = item.extension.exports
if (!exp || typeof exp[method] !== 'function') throw new Error(`method ${method} unavailable`)

Type guard

function hasMethod(exp: unknown, method: string): exp is Record<string, (...args: unknown[]) => unknown> {
  return typeof exp === 'object' && exp !== null &&
    typeof (exp as Record<string, unknown>)[method] === 'function'
}

Try / catch

try {
  return await manager.call(id, method, args)
} catch (e) {
  if (/method .* not found on extension/.test(e.message)) {
    logger.warn(`API mismatch with ${id}: ${method}`)
    return fallbackValue
  }
  throw e
}

Prevention

When it happens

Trigger: Calling manager.call(id, 'activateX', ...) where the extension's exports lacks activateX, exports is null/undefined despite being active, or the method exists but is not a function.

Common situations: Extension version drift: the caller targets an API method removed/renamed in a newer extension release; extension's activate() returned nothing (exports undefined); passing the wrong method name casing.

Related errors


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