Playwright's selectOption does not work when connected to a Browserbase session with advancedStealth: true. The selected option's value is empty and selectedIndex is -1 from the page's JavaScript perspective, even though Playwright reports success.
Playwright's selectOption runs in Chrome's utility world (an isolated JavaScript execution context). It sets option.selected = true on the target <option> element, then dispatches change and input events.
With Browserbase's advancedStealth: true, the option.selected = true property mutation made in the utility world does not propagate to the main world. When the page's event handlers fire and read select.value, they see "".
Notably:
select.value = undefined(HTMLSelectElement setter) DOES cross worlds — it successfully clears the previous selectionoption.selected = true(HTMLOptionElement setter) does NOT cross worlds
Without advancedStealth (or on a local Playwright browser), both property mutations propagate correctly.
A minimal page with a <select> and two event listeners (direct + delegated on document).
# 1. Host the test page
python3 -m http.server 8765
# 2. Run with local Playwright (PASS)
npm install
PAGE_URL=http://localhost:8765/ node repro.js --local
# 3. Run with Browserbase (stealth FAILS, no-stealth PASSES)
BROWSERBASE_API_KEY=... BROWSERBASE_PROJECT_ID=... PAGE_URL=http://localhost:8765/ node repro.js=== Local Playwright (no stealth) ===
Direct handler: {"value":"A","selectedIndex":1,"optionSelected":true,"isTrusted":false}
Delegated handler: {"value":"A","selectedIndex":1,"optionSelected":true,"isTrusted":false}
DOM state: {"value":"A","selectedIndex":1,"options":["0:false=","1:true=A","2:false=B"]}
PASS ✓
=== Browserbase (advancedStealth: false) ===
...
PASS ✓
=== Browserbase (advancedStealth: true) ===
Direct handler: {"value":"","selectedIndex":-1,"optionSelected":false,"isTrusted":false}
Delegated handler: {"value":"","selectedIndex":-1,"optionSelected":false,"isTrusted":false}
DOM state: {"value":"","selectedIndex":-1,"options":["0:false=","1:false=A","2:false=B"]}
FAIL ✗ — value is empty, option.selected did not propagate
Any page with a JavaScript change event handler that reads select.value synchronously will see an empty value when selectOption is used through Browserbase with advanced stealth. This breaks:
- jQuery delegated event handlers (
$(document).on('change', 'select', handler)) - Any framework that manages form state via change events (e.g., government portal frameworks like FWDC)
- Direct event listeners that read
e.target.value
Use page.evaluate to set the value in the main world instead of Playwright's selectOption:
await page.evaluate(([selector, value]) => {
const el = document.querySelector(selector);
const norm = s => s.replace(/\s+/g, ' ').trim().toLowerCase();
for (const opt of el.options) {
if (norm(opt.text) === norm(value) || norm(opt.value) === norm(value)) {
el.value = opt.value;
el.dispatchEvent(new Event('change', { bubbles: true }));
return;
}
}
}, ['#myselect', 'Alpha']);This works because page.evaluate runs in the main world, where property mutations are immediately visible to all event handlers.