{"record":{"id":"53a843c33286517c","repo":"microsoft/playwright","slug":"engine-name-selector-engine-has-been-already","errorCode":null,"errorMessage":"\"${engine.name}\" selector engine has been already registered","messagePattern":"\"(.+?)\" selector engine has been already registered","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/selectors.ts","lineNumber":65,"sourceCode":"      'aria-ref'\n    ]);\n    this._builtinEnginesInMainWorld = new Set([\n      '_react', '_vue',\n    ]);\n    this._engines = new Map();\n    this._testIdAttributeName = testIdAttributeName ?? 'data-testid';\n    for (const engine of engines)\n      this.register(engine);\n  }\n\n  register(engine: channels.SelectorEngine) {\n    if (!engine.name.match(/^[a-zA-Z_0-9-]+$/))\n      throw new Error('Selector engine name may only contain [a-zA-Z0-9_] characters');\n    // Note: we keep 'zs' for future use.\n    if (this._builtinEngines.has(engine.name) || engine.name === 'zs' || engine.name === 'zs:light')\n      throw new Error(`\"${engine.name}\" is a predefined selector engine`);\n    if (this._engines.has(engine.name))\n      throw new Error(`\"${engine.name}\" selector engine has been already registered`);\n    this._engines.set(engine.name, engine);\n  }\n\n  testIdAttributeName(): string {\n    return this._testIdAttributeName;\n  }\n\n  setTestIdAttributeName(testIdAttributeName: string) {\n    this._testIdAttributeName = testIdAttributeName;\n  }\n\n  parseSelector(selector: string | ParsedSelector, strict: boolean) {\n    const parsed = typeof selector === 'string' ? parseSelector(selector) : selector;\n    let needsMainWorld = false;\n    visitAllSelectorParts(parsed, part => {\n      const name = part.name;\n      const custom = this._engines.get(name);\n      if (!custom && !this._builtinEngines.has(name))","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/selectors.ts#L47-L83","documentation":"Thrown by Selectors.register when the engine name was already added to the _engines map by a prior successful register() call. Each custom engine name must be unique within a Selectors instance; re-registering is treated as a programming error rather than an update.","triggerScenarios":"Calling selectors.register(engine) twice with the same name; a setup module that runs register() being imported more than once (e.g. HMR reload, duplicate imports) without guarding.","commonSituations":"Hot-module reloading re-running a registration module; a shared helper that registers the same engine in multiple test files that all load; copy-paste registering 'foo' in two places.","solutions":["Guard registration: only register if the engine is not already present (check before calling register).","Centralize engine registration in a single module imported once at startup.","Use a Set/flag to make registration idempotent across HMR reloads."],"exampleFix":"// before\nselectors.register({ name: 'my-engine', query: () => {...} });\n// imported again elsewhere → throws\n\n// after — idempotent guard\nif (!selectors._engines.has('my-engine'))\n  selectors.register({ name: 'my-engine', query: () => {...} });","handlingStrategy":"validation","validationCode":"// Guard against double registration (e.g. under HMR)\nfunction registerOnce(selectors: any, engine: any) {\n  if (!selectors._engines.has(engine.name))\n    selectors.register(engine);\n}\n\nregisterOnce(selectors, { name: 'my-engine', query: () => {/*...*/} });","typeGuard":"function isEngineRegistered(selectors: any, name: string): boolean {\n  return selectors._engines.has(name);\n}","tryCatchPattern":"try {\n  selectors.register(engine);\n} catch (e) {\n  if (/has been already registered/.test(e.message)) return; // idempotent\n  throw e;\n}","preventionTips":["Centralize engine registration in one module imported a single time.","Guard register() with a has() check to make it idempotent under HMR/re-imports.","Track registered names in a Set to avoid duplicate calls across test files."],"tags":["selectors","registration","duplicate","custom-engine"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}