{"record":{"id":"3128ceffbd872923","repo":"neoclide/coc.nvim","slug":"keymap-name-already-exists","errorCode":null,"errorMessage":"keymap: \"${name}\" already exists.","messagePattern":"keymap: \"(.+?)\" already exists\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/core/keymaps.ts","lineNumber":79,"sourceCode":"  }\n\n  public async doInsertKeymap(key: string, ...args: any[]): Promise<InsertKeymapResult> {\n    let fn = this.insertKeymaps.get(key)\n    if (!fn) {\n      logger.error(`insert keymap for ${key} not found`)\n      return []\n    }\n    let res = await Promise.resolve(fn(...args))\n    return Array.isArray(res) ? res : []\n  }\n\n  /**\n   * Register global <Plug>(coc-${key}) key mapping.\n   */\n  public registerKeymap(modes: MapMode[], name: string, fn: KeymapCallback, opts: KeymapOption = {}): Disposable {\n    if (!name) throw new Error(`Invalid key ${name} of registerKeymap`)\n    let key = `coc-${name}`\n    if (this.keymaps.has(key)) throw new Error(`keymap: \"${name}\" already exists.`)\n    const lhs = `<Plug>(${key})`\n    opts = Object.assign({ sync: true, cancel: true, silent: true, repeat: false }, opts)\n    let { nvim } = this\n    this.keymaps.set(key, [fn, !!opts.repeat])\n    let method = opts.sync ? 'request' : 'notify'\n    for (let mode of modes) {\n      if (mode == 'i') {\n        const cancel = opts.cancel ? 1 : 0\n        nvim.setKeymap(mode, lhs, `coc#_insert_key('${method}', '${key}', ${cancel})`, {\n          expr: true,\n          noremap: true,\n          silent: opts.silent\n        })\n      } else {\n        nvim.setKeymap(mode, lhs, `:${getKeymapModifier(mode, opts.cmd)}call coc#rpc#${method}('doKeymap', ['${key}'])<cr>`, {\n          noremap: true,\n          silent: opts.silent\n        })","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/core/keymaps.ts#L61-L97","documentation":"Each coc keymap name must be unique: registerKeymap stores handlers under 'coc-<name>' and throws if that key already exists, preventing silent override of an existing <Plug>(coc-<name>) mapping.","triggerScenarios":"Calling registerKeymap twice with the same name, e.g. an extension activating twice without disposing the previous Disposable, or two extensions claiming the same keymap name.","commonSituations":"Double activation after extension reload without cleanup; re-running init code in tests; plugin conflicts where two extensions register the same coc-<name> keymap.","solutions":["Keep the Disposable returned by registerKeymap and dispose it before re-registering","Register keymaps only once in extension activation (guard against re-activation)","Choose a unique name to avoid collision with other extensions"],"exampleFix":"// before\nkeymaps.registerKeymap(['n'], 'myaction', handler)\nkeymaps.registerKeymap(['n'], 'myaction', handler2) // throws\n// after\ndisposables.push(keymaps.registerKeymap(['n'], 'myaction', handler))\n// re-registration: dispose previous first\n","handlingStrategy":"try-catch","validationCode":"// registry is internal, but you can guard your own registrations\nif (registeredNames.has(name)) return // already registered this session","typeGuard":null,"tryCatchPattern":"try {\n  disposables.push(keymaps.registerKeymap(['n'], name, handler))\n} catch (e) {\n  if (String(e.message).includes('already exists')) logger.warn(`keymap ${name} already registered`)\n  else throw e\n}","preventionTips":["Always keep and dispose the Disposable returned by registerKeymap","Register keymaps exactly once per activation","Use unique, extension-prefixed keymap names","Deactivate properly so re-activation does not duplicate registrations"],"tags":["keymap","duplicate-registration","lifecycle"],"backgroundTag":"duplicate-registration","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}