{"id":"f1a3151a5c48e265","repo":"jestjs/jest","slug":"watch-plugin-configuration-error","errorCode":null,"errorMessage":"Watch plugin configuration error","messagePattern":"Watch plugin configuration error","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"packages/jest-core/src/watch.ts","lineNumber":514,"sourceCode":"  Watch plugin ${chalk.bold.red(\n    getPluginIdentifier(plugin),\n  )} attempted to register key ${chalk.bold.red(`<${key}>`)},\n  that is reserved internally for ${chalk.bold.red(\n    conflictor.forbiddenOverwriteMessage,\n  )}.\n  Please change the configuration key for this plugin.`.trim();\n  } else {\n    const plugins = [conflictor.plugin, plugin]\n      .map(p => chalk.bold.red(getPluginIdentifier(p)))\n      .join(' and ');\n    error = `\n  Watch plugins ${plugins} both attempted to register key ${chalk.bold.red(\n    `<${key}>`,\n  )}.\n  Please change the key configuration for one of the conflicting plugins to avoid overlap.`.trim();\n  }\n\n  throw new ValidationError('Watch plugin configuration error', error);\n};\n\nconst getPluginIdentifier = (plugin: WatchPlugin) =>\n  // This breaks as `displayName` is not defined as a static, but since\n  // WatchPlugin is an interface, and it is my understanding interface\n  // static fields are not definable anymore, no idea how to circumvent\n  // this :-(\n  // @ts-expect-error: leave `displayName` be.\n  plugin.constructor.displayName || plugin.constructor.name;\n\nconst getPluginKey = (\n  plugin: WatchPlugin,\n  globalConfig: Config.GlobalConfig,\n) => {\n  if (typeof plugin.getUsageInfo === 'function') {\n    return (plugin.getUsageInfo(globalConfig) || {key: null}).key;\n  }\n","sourceCodeStart":496,"sourceCodeEnd":532,"githubUrl":"https://github.com/jestjs/jest/blob/f49721c78e195558b40913977c9230f5b7f559d8/packages/jest-core/src/watch.ts#L496-L532","documentation":"Thrown as a ValidationError from checkForConflicts when registering watch plugins: either two plugins claim the same key, or a plugin tries to register a key reserved internally (forbiddenOverwriteMessage). Jest aborts watch-mode setup because ambiguous key bindings would make the interactive menu unusable.","triggerScenarios":"Configuring `watchPlugins` with two entries whose `getUsageInfo().key` collide (e.g. both return `'t'`), or a third-party watch plugin that hard-codes a key Jest reserves (a/p/f/q/t/o/enter/etc.).","commonSituations":"Adding `jest-watch-typeahead` alongside another plugin that also binds the same letter; a plugin version change that introduced a default key clash; mixing multiple watch plugins without explicit key config.","solutions":["Read the conflict text — it names both plugins and the conflicting `<key>`.","Pass an explicit `key` in the watchPlugins config tuple for the offender, e.g. `['jest-watch-typeahead/filename', { key: 'f' }]`.","Disable or remove one of the colliding plugins.","Upgrade the plugin to a version that allows key overrides, and avoid reserved keys listed in Jest's usage menu."],"exampleFix":"// before\nwatchPlugins: [\n  ['jest-watch-typeahead/filename'],\n  ['my-custom-plugin', { key: 'f' }], // both want 'f'\n]\n\n// after\nwatchPlugins: [\n  ['jest-watch-typeahead/filename', { key: 'f' }],\n  ['my-custom-plugin', { key: 'm' }],\n]","handlingStrategy":"validation","validationCode":"// dedupe watch plugin keys in config before passing to Jest\nconst keys = watchPlugins.map(p => (p[1] && p[1].key) || require(p[0]).default.key);\nif (new Set(keys).size !== keys.length) throw new Error('duplicate watch plugin keys');","typeGuard":"const hasUniqueKeys = (plugins: Array<[string, {key?: string}]>) => new Set(plugins.map(([, o]) => o?.key).filter(Boolean)).size === plugins.filter(([, o]) => o?.key).length;","tryCatchPattern":null,"preventionTips":["Always pass an explicit `key` for every watch plugin entry.","Avoid single-letter keys already used by Jest's menu (a,p,f,t,o,q,enter,etc.).","Upgrade watch plugins together so key conflicts surface."],"tags":["jest-core","watch-mode","watch-plugins","config"],"analyzedSha":"f49721c78e195558b40913977c9230f5b7f559d8","analyzedAt":"2026-08-03T20:16:28.571Z","schemaVersion":2}