You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: page.evaluate with exposeFunctions and world: "utility" fails with an internal TypeError #42686
1.64.0-next. Originally reproduced at aeebee1d2; packages/playwright-core/src/client/jsHandle.ts has not been touched since that commit, so it still applies.
Steps to reproduce
exposeFunctions and the world option added in #42635 cannot be combined, but passing both crashes inside the injected code rather than saying so.
Same error from frame.evaluate and locator.evaluate. Reproduced on chromium, firefox and webkit.
Why it happens
PageBinding.createInitScript installs the bindings controller as an init script, and init scripts only run in the main world, so globalThis[kBindingsControllerProperty] is undefined in the utility world. Runtime.addBinding is registered without a world, so the transport is there but the controller that dispatches through it is not.
That also makes a documented claim false. docs/src/api/params.md says an exposed function is "technically accessible from all frames and worlds of the page", in both js-evaluate-expose-functions and js-init-script-expose-functions:
Either make bindings reachable from the utility world, or reject the combination with a clear error the way JSHandleDispatcher.evaluateExpression already does for a world it cannot serve. Either way the two doc sentences want correcting, since they promise worlds today.
I had a patch for the second option in #42649 with the guard in assertEvaluateOptions, which every evaluate entry point already calls, plus the doc correction and one test. Happy to reopen that, rework it toward the first option, or leave it entirely, whichever you prefer. I have no view on which layer you want this solved at.
I am a freshman in college trying to make myself useful on real projects, so if I have misread the intent here just say and I will drop it.
Hi, I'm the Playwright bot and I took a first look at this.
Reproduced on tip-of-tree (1.64.0-next, sha 52a0e72), on macOS, in chromium, firefox and webkit. Your analysis holds on both counts: { exposeFunctions: true, world: 'utility' } fails inside the injected code with TypeError: Cannot read properties of undefined (reading 'callBinding') (firefox words it as can't access property "callBinding", globalThis[kBindingsControllerProperty] is undefined), and an already-exposed function is genuinely not visible from the utility world — the second page.evaluate returns 'NOT PRESENT', not 42, so the "accessible from all frames and worlds of the page" sentence in docs/src/api/params.md (lines 591 and 597) is wrong as written.
Which of the two directions to take — reach bindings into the utility world, or reject the combination — is a maintainer call, so I'll leave that one alone.
Minimal repro
test('exposeFunctions cannot be combined with world: utility',async({ page })=>{// page.evaluate: TypeError: Cannot read properties of undefined (reading 'callBinding')constresult=awaitpage.evaluate(async({ cb })=>awaitcb(17),{cb: (x: number)=>x*2},{exposeFunctions: true,world: 'utility'});expect(result).toBe(34);});test('exposeFunction is not reachable from the utility world',async({ page })=>{awaitpage.exposeFunction('f',(x: number)=>x*2);expect(awaitpage.evaluate(()=>window.f?.(21)??'NOT PRESENT')).toBe(42);// Received: "NOT PRESENT"expect(awaitpage.evaluate(()=>window.f?.(21)??'NOT PRESENT',undefined,{world: 'utility'})).toBe(42);});
What I ran
version: tip-of-tree 1.64.0-next, sha 52a0e72 (checkout built from source); OS: macOS
browsers: chromium, firefox, webkit — 9/9 failures, no divergence beyond the engine's wording of the TypeError
entry points: page.evaluate, frame.evaluate (page.mainFrame()) and locator.evaluate — all three throw the same callBinding error, as you reported
the separate page.exposeFunction + world: 'utility' case fails as an assertion ('NOT PRESENT' instead of 42), i.e. no error, just a missing binding
ran as tests/page/ specs against the local build with --retries=0 and with retries; identical every time
Triaged on macOS by the Playwright bot - agent run
Activity
github-actions commentedon Sep 14, 2026
Hi, I'm the Playwright bot and I took a first look at this.
Reproduced on tip-of-tree (
1.64.0-next, sha52a0e72), on macOS, in chromium, firefox and webkit. Your analysis holds on both counts:{ exposeFunctions: true, world: 'utility' }fails inside the injected code withTypeError: Cannot read properties of undefined (reading 'callBinding')(firefox words it ascan't access property "callBinding", globalThis[kBindingsControllerProperty] is undefined), and an already-exposed function is genuinely not visible from the utility world — the secondpage.evaluatereturns'NOT PRESENT', not42, so the "accessible from all frames and worlds of the page" sentence indocs/src/api/params.md(lines 591 and 597) is wrong as written.Which of the two directions to take — reach bindings into the utility world, or reject the combination — is a maintainer call, so I'll leave that one alone.
Minimal repro
What I ran
1.64.0-next, sha52a0e72(checkout built from source); OS: macOSTypeErrorpage.evaluate,frame.evaluate(page.mainFrame()) andlocator.evaluate— all three throw the samecallBindingerror, as you reportedpage.exposeFunction+world: 'utility'case fails as an assertion ('NOT PRESENT'instead of42), i.e. no error, just a missing bindingtests/page/specs against the local build with--retries=0and with retries; identical every timeTriaged on macOS by the Playwright bot - agent run