neoclide/coc.nvim · error

Invalid access to exports, extension "${id}" not activated

Error message

Invalid access to exports, extension "${id}" not activated

What it means

The Extension object exposed by ExtensionManager defines a getter for `exports` that throws `Invalid access to exports, extension "<id>" not activated` whenever the extension has not finished activating. This guards the VS Code-like contract that extension.exports is only meaningful after activate() resolves, preventing callers from reading partial/undefined exports.

Source

Thrown at src/extension/manager.ts:523

          } catch (e) {
            logger.error(`Error on active extension ${id}:`, e)
            reject(e as Error)
          }
        })
        return result
      },
      id,
      packageJSON,
      extensionPath,
      extensionUri: URI.file(extensionPath),
      get isActive() {
        return isActive
      },
      get module() {
        return ext
      },
      get exports() {
        if (!isActive) throw new Error(`Invalid access to exports, extension "${id}" not activated`)
        return exports
      }
    }
    Object.freeze(extension)
    this.extensions.set(id, {
      id,
      type: extensionType,
      isLocal: extensionType == ExtensionType.Local,
      extension,
      directory: root,
      filepath: filename,
      events: getEvents(packageJSON.activationEvents),
      deactivate: async () => {
        if (!isActive) return
        isActive = false
        result = undefined
        exports = undefined
        disposeExtension(id)

View on GitHub (pinned to 50e974d969)

Solutions

  1. Check extension.isActive before touching exports, and await extension.activate() when it is not active
  2. Use ExtensionManager.call(id, method, args), which activates automatically, instead of reading exports directly
  3. Subscribe to the extension activation event and access exports only in the callback
  4. If exports should exist immediately, change the extension to an eagerly activated type

Example fix

// before
const ext = extensions.getExtension('coc-x')
const api = ext.exports // throws if not activated
// after
const ext = extensions.getExtension('coc-x')
if (!ext.isActive) await ext.activate()
const api = ext.exports
Defensive patterns

Strategy: try-catch

Validate before calling

const ext = extensions.getExtension(id)
if (!ext.isActive) await ext.activate() // ensures exports getter is safe

Type guard

function activatedExports<T>(ext: { isActive: boolean; activate(): Promise<T>; exports: T }): T | undefined {
  return ext.isActive ? ext.exports : undefined
}

Try / catch

let api
try {
  api = ext.exports
} catch (e) {
  if (/Invalid access to exports/.test(e.message)) {
    await ext.activate()
    api = ext.exports
  } else throw e
}

Prevention

When it happens

Trigger: Synchronously accessing extension.exports right after getExtension()/onDidExtension activation-event registration but before activation completes; accessing exports inside an activation handler before await activate() resolves; any code path reading exports of a lazily-activated extension.

Common situations: Plugin code doing `const api = extensions.getExtension('coc-x').exports` without awaiting activation; using exports in an onActivation callback that runs before the extension's own activate finishes.

Related errors


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