{"record":{"id":"ddd0c4a8070b21e2","repo":"microsoft/playwright","slug":"function-name-has-been-already-registered-in-ddd0c4","errorCode":null,"errorMessage":"Function \"${name}\" has been already registered in the browser context","messagePattern":"Function \"(.+?)\" has been already registered in the browser context","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/page.ts","lineNumber":357,"sourceCode":"  }\n\n  opener(): Page | undefined {\n    return this._opener;\n  }\n\n  mainFrame(): frames.Frame {\n    return this.frameManager.mainFrame();\n  }\n\n  frames(): frames.Frame[] {\n    return this.frameManager.frames();\n  }\n\n  async exposeBinding(progress: Progress, name: string, playwrightBinding: frames.FunctionWithSource, noGlobal?: boolean): Promise<PageBinding> {\n    if (this._pageBindings.has(name))\n      throw new Error(`Function \"${name}\" has been already registered`);\n    if (this.browserContext._pageBindings.has(name))\n      throw new Error(`Function \"${name}\" has been already registered in the browser context`);\n    await progress.race(this.browserContext.exposePlaywrightBindingIfNeeded());\n    const binding = new PageBinding(this, name, playwrightBinding, noGlobal);\n    this._pageBindings.set(name, binding);\n    try {\n      await progress.race(this.delegate.addInitScript(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\n  async removeExposedBinding(binding: PageBinding) {\n    if (this._pageBindings.get(binding.name) !== binding)\n      return;\n    this._pageBindings.delete(binding.name);\n    await this.delegate.removeInitScripts([binding.initScript]);","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/page.ts#L339-L375","documentation":"Thrown by Page.exposeBinding when no page-level duplicate exists but a BrowserContext-level binding with the same name was already registered. Playwright treats context bindings as inherited by every page, so adding a page binding with a colliding name would shadow the context one ambiguously and is rejected.","triggerScenarios":"Calling browserContext.exposeFunction('foo', fn) and then page.exposeFunction('foo', fn) on a page in that context; mixing context-wide and page-wide bindings that share a name; a shared helper that adds page bindings on top of an app that already exposes the same name at context scope.","commonSituations":"A test framework fixture exposes a helper at context scope while a legacy per-page helper still calls page.exposeFunction with the same name; upgrading code from per-page to per-context exposure without removing the old call.","solutions":["Drop the page-level exposeFunction call and rely on the context-level binding instead.","If a distinct page binding is genuinely needed, rename it so it does not collide with the context binding.","Centralize all binding registration in one place (preferably context scope) and remove duplicates elsewhere."],"exampleFix":"// before\nawait context.exposeFunction('getToken', () => token);\nawait page.exposeFunction('getToken', () => other); // throws\n// after\nawait context.exposeFunction('getToken', () => token); // pages inherit it","handlingStrategy":"validation","validationCode":"// Decide scope up front and never mix names across scopes\nasync function exposeScoped(context, page, name, fn, scope) {\n  if (scope === 'context')\n    return context.exposeFunction(name, fn); // pages inherit, no per-page call needed\n  // page scope: ensure no context binding collides by tracking context names yourself\n  return page.exposeFunction(name, fn);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize binding registration in one module to avoid context/page name collisions.","When promoting a binding from page to context scope, delete the old page-level call.","Namespace binding names by feature to make collisions obvious."],"tags":["bindings","expose-function","browser-context","duplicate-name"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}