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
- Check extension.isActive before touching exports, and await extension.activate() when it is not active
- Use ExtensionManager.call(id, method, args), which activates automatically, instead of reading exports directly
- Subscribe to the extension activation event and access exports only in the callback
- 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
- Always await activate() before reading exports
- Access exports only inside activation callbacks/events
- Prefer manager.call() which handles lazy activation
- Never cache exports references across deactivation cycles
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.