{"record":{"id":"7193a9f4a922be56","repo":"microsoft/playwright","slug":"function-name-has-been-already-registered","errorCode":null,"errorMessage":"Function \"${name}\" has been already registered","messagePattern":"Function \"(.+?)\" has been already registered","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/browserContext.ts","lineNumber":379,"sourceCode":"  async exposePlaywrightBindingIfNeeded() {\n    this._playwrightBindingExposed ??= (async () => {\n      await this.doExposePlaywrightBinding();\n\n      this.bindingsInitScript = PageBinding.createInitScript(this);\n      this.initScripts.push(this.bindingsInitScript);\n      await this.doAddInitScript(this.bindingsInitScript);\n      await this.safeNonStallingEvaluateInAllFrames(this.bindingsInitScript.source, 'main');\n    })();\n    return await this._playwrightBindingExposed;\n  }\n\n  needsPlaywrightBinding(): boolean {\n    return this._playwrightBindingExposed !== undefined;\n  }\n\n  async exposeBinding(progress: Progress, name: string, playwrightBinding: frames.FunctionWithSource, forClient?: unknown, noGlobal?: boolean): Promise<PageBinding> {\n    if (this._pageBindings.has(name))\n      throw new Error(`Function \"${name}\" has been already registered`);\n    for (const page of this.pages()) {\n      if (page.getBinding(name))\n        throw new Error(`Function \"${name}\" has been already registered in one of the pages`);\n    }\n    await progress.race(this.exposePlaywrightBindingIfNeeded());\n    const binding = new PageBinding(this, name, playwrightBinding, noGlobal);\n    binding.forClient = forClient;\n    this._pageBindings.set(name, binding);\n    try {\n      await progress.race(this.doAddInitScript(binding.initScript));\n      await progress.race(this.safeNonStallingEvaluateInAllFrames(binding.initScript.source, 'main'));\n      return binding;\n    } catch (error) {\n      this._pageBindings.delete(name);\n      throw error;\n    }\n  }\n","sourceCodeStart":361,"sourceCodeEnd":397,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/browserContext.ts#L361-L397","documentation":"`exposeBinding`/`exposeFunction` on a BrowserContext checks `_pageBindings` for the name; a duplicate name at context level is rejected because the global binding would collide.","triggerScenarios":"`context.exposeBinding('foo', ...)` called twice with the same name; or re-exposing a name already registered via `context.exposeFunction` without disposing first.","commonSituations":"Test fixtures that expose a binding per test without cleanup; helper modules registering the same binding name across suites; refactoring page-level bindings to context-level.","solutions":["Use the Disposable returned by exposeBinding and dispose before re-exposing the same name","Use a unique name per binding (namespace by test/module)","Expose at page level if you need per-page isolation","Track registrations in a Set to avoid duplicates"],"exampleFix":"// before\nawait context.exposeBinding('cb', h);\nawait context.exposeBinding('cb', h); // throws\n\n// after\nconst d1 = await context.exposeBinding('cb', h);\nawait d1.dispose();\nawait context.exposeBinding('cb', h); // ok","handlingStrategy":"validation","validationCode":"const registered = new Set<string>();\nasync function exposeOnce(ctx: BrowserContext, name: string, fn: Function) {\n  if (registered.has(name)) return;\n  registered.add(name);\n  const d = await ctx.exposeBinding(name, fn as any);\n  d[Symbol.dispose] && (d as any)[Symbol.dispose]();\n}\n// Prefer: dispose before re-registering the same name.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Dispose the Disposable returned by exposeBinding before re-exposing a name","Namespace binding names per test/module to avoid collisions","Expose at context level once in setup, not per test"],"tags":["browser-context","expose-binding","duplicate"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}