{"record":{"id":"75fc38068d4ad75b","repo":"neoclide/coc.nvim","slug":"method-method-not-found-on-extension-id","errorCode":null,"errorMessage":"method ${method} not found on extension ${id}","messagePattern":"method (.+?) not found on extension (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/extension/manager.ts","lineNumber":367,"sourceCode":"      throw new Error(`Extension ${id} not registered`)\n    }\n    if (item.type == ExtensionType.SingleFile) {\n      await this.loadExtensionFile(item.filepath)\n    } else {\n      await this.loadExtension(item.directory)\n    }\n  }\n\n  public async call(id: string, method: string, args: any[]): Promise<any> {\n    let item = this.extensions.get(id)\n    if (!item) throw new Error(`extension ${id} not registered`)\n    let { extension } = item\n    if (!extension.isActive) {\n      await this.activate(id)\n    }\n    let { exports } = extension\n    if (!exports || typeof exports[method] !== 'function') {\n      throw new Error(`method ${method} not found on extension ${id}`)\n    }\n    return await Promise.resolve(exports[method].apply(null, args))\n  }\n\n  public registContribution(id: string, packageJSON: any, directory: string, filepath?: string): void {\n    let { contributes, activationEvents } = packageJSON\n    let { configuration, rootPatterns, commands } = contributes ?? {}\n    let definitions: IStringDictionary<IJSONSchema> | undefined\n    let props = getProperties(configuration ?? {})\n    if (!isEmpty(props)) {\n      // /configuration\n      let properties = convertProperties(props, ConfigurationScope.WINDOW)\n      if (Is.objectLiteral(configuration.definitions)) {\n        let prefix = id.replace(/[^\\w]/g, '')\n        const addPrefix = (obj: object, key: string) => {\n          if (key == '$ref') {\n            let val = obj[key]\n            if (Is.string(val) && val.startsWith('#/definitions/')) {","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/extension/manager.ts#L349-L385","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the extension's activate() return value — ensure it returns an object containing the method you call","Align caller and extension versions: the method may have been renamed/removed — consult the extension's API docs","Verify exact method name and casing","If you own the extension, export the method: `return { myMethod: async (...args) => {...} }` from activate()"],"exampleFix":"// before (extension side)\nexports.activate = async () => { /* returns undefined */ }\n// after\nexports.activate = async () => ({ run: async () => 'ok' })","handlingStrategy":"try-catch","validationCode":"const item = manager.getExtension(id)\nif (!item?.isActive) await manager.activate(id)\nconst exp = item.extension.exports\nif (!exp || typeof exp[method] !== 'function') throw new Error(`method ${method} unavailable`)","typeGuard":"function hasMethod(exp: unknown, method: string): exp is Record<string, (...args: unknown[]) => unknown> {\n  return typeof exp === 'object' && exp !== null &&\n    typeof (exp as Record<string, unknown>)[method] === 'function'\n}","tryCatchPattern":"try {\n  return await manager.call(id, method, args)\n} catch (e) {\n  if (/method .* not found on extension/.test(e.message)) {\n    logger.warn(`API mismatch with ${id}: ${method}`)\n    return fallbackValue\n  }\n  throw e\n}","preventionTips":["Pin compatible extension versions alongside the calling code","Check the extension's exported API docs before calling new methods","Ensure activate() always returns the exports object","Feature-detect methods instead of assuming they exist across versions"],"tags":["extension-api","exports","contract-mismatch"],"backgroundTag":"method-not-found","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}