{"record":{"id":"a568040e218bbad5","repo":"neoclide/coc.nvim","slug":"action-key-already-exists","errorCode":null,"errorMessage":"Action ${key} already exists","messagePattern":"Action (.+?) already exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/plugin.ts","lineNumber":218,"sourceCode":"    this.addAction('nextEditAvailable', () => this.handler.nextEdit.available())\n    this.addAction('notificationHistory', () => window.notifications.history)\n  }\n\n  public get workspace(): Workspace {\n    return workspace\n  }\n\n  public get window(): Window {\n    return window\n  }\n\n  public get completion(): Completion {\n    return completion\n  }\n\n  public addAction(key: string, fn: Callback, alias?: string): void {\n    if (this.actions.has(key)) {\n      throw new Error(`Action ${key} already exists`)\n    }\n    this.actions.set(key, fn)\n    if (alias) this.actions.set(alias, fn)\n  }\n\n  public async init(rtp: string, mcpStarted = false): Promise<void> {\n    if (this.initialized) return\n    this.initialized = true\n    let { nvim } = this\n    await extensions.init(rtp)\n    await workspace.init(window)\n    nvim.setVar('coc_workspace_initialized', true, true)\n    snippetManager.init()\n    services.init()\n    sources.init()\n    languages.sources = sources\n    completion.init()\n    diagnosticManager.init()","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/plugin.ts#L200-L236","documentation":"Plugin.addAction() registers a Vim-callable action in an internal Map keyed by name (plus optional alias). It refuses to overwrite an existing key and throws this error, protecting against accidental action replacement.","triggerScenarios":"Calling plugin.addAction('myAction', fn) (or an alias) when 'myAction' is already registered — e.g. registering the same action twice during init/reload, or picking a key that collides with a built-in action like 'symbol' or 'definition'.","commonSituations":"Extensions registering actions on every activation without idempotency (coc restarts or extension reload re-runs registration); two extensions choosing the same action name; re-sourcing a config that calls addAction again.","solutions":["Check plugin.hasAction(key) before calling addAction, or make registration idempotent","Wrap addAction in try/catch to tolerate duplicate registration on reload","Choose a unique, extension-prefixed action key (e.g. 'myext.format')","Ensure the registration code runs only once per plugin lifecycle"],"exampleFix":"// before\nplugin.addAction('myAction', handler) // throws on reload\n// after\nif (!plugin.hasAction('myAction')) plugin.addAction('myAction', handler)","handlingStrategy":"validation","validationCode":"if (plugin.hasAction('myAction')) return\nplugin.addAction('myAction', handler)","typeGuard":null,"tryCatchPattern":"try {\n  plugin.addAction('myAction', handler)\n} catch (e) {\n  if (e.message.includes('already exists')) {\n    // already registered (e.g. after reload); safe to ignore\n  }\n}","preventionTips":["Make action registration idempotent with hasAction checks","Prefix action keys with your extension name to avoid collisions","Run registration only once per activation lifecycle"],"tags":["plugin","action-registration","duplicate-key"],"backgroundTag":"duplicate-identifier-registration","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}