Patchright version: 1.59.1
Python version: 3.12.12
OS: macOS 25.1.0 (also observed on Linux)
Reproducer
import asyncio
from pathlib import Path
from patchright.async_api import async_playwright
async def main() -> None:
async with async_playwright() as pw:
ctx = await pw.chromium.launch_persistent_context(
user_data_dir=str(Path("/tmp/patchright-repro").resolve()),
headless=True,
args=["--headless=new"],
)
page = ctx.pages[0]
# The ONE line below reliably breaks the first navigate.
await ctx.add_init_script("window.__x = 1;")
try:
resp = await page.goto("https://httpbin.org/get")
print("status:", resp.status if resp else None)
finally:
await ctx.close()
asyncio.run(main())
Expected: goto returns 200 (or any network status).
Actual: goto raises playwright._impl._errors.Error: net::ERR_NAME_NOT_RESOLVED at https://httpbin.org/get 100% of the time.
Observations
- Removing the
add_init_script call: navigate succeeds.
- Replacing the script body with
"" or any other content: same failure.
- Registering on
page instead of ctx: same failure.
- Loading
about:blank first then navigating: same failure.
- Affects every
launch_persistent_context session, regardless of profile state (fresh dir or warm dir).
- Plain Playwright (
playwright.async_api) is NOT affected by the same pattern.
Impact
Any consumer that wants stealth + operator-supplied init scripts (instrumentation hooks, console capture, fingerprint shims) on patchright is forced to choose one or the other. Our project (browser-driver MCP) had to ship a workaround that drops operator init_scripts with a warning on patchright/cloak engines, and skip our own JS-layer Sec-CH-UA navigator.userAgentData override on those engines too, both because the workaround applies uniformly to every context.add_init_script call post-launch.
Suspected area
Looks like a race between patchright's own stealth init scripts being registered on the persistent context's default page and any subsequent add_init_script call mutating the context's init-script set while the page's network stack is still settling. The persistent context returns with the default page already open, which is what makes this different from the plain Playwright launch + new_context pattern.
Happy to test a patch; we can validate the fix against our smoke-test session against httpbin and against a Cloudflare-fronted target.
Patchright version: 1.59.1
Python version: 3.12.12
OS: macOS 25.1.0 (also observed on Linux)
Reproducer
Expected:
gotoreturns 200 (or any network status).Actual:
gotoraisesplaywright._impl._errors.Error: net::ERR_NAME_NOT_RESOLVED at https://httpbin.org/get100% of the time.Observations
add_init_scriptcall: navigate succeeds.""or any other content: same failure.pageinstead ofctx: same failure.about:blankfirst then navigating: same failure.launch_persistent_contextsession, regardless of profile state (fresh dir or warm dir).playwright.async_api) is NOT affected by the same pattern.Impact
Any consumer that wants stealth + operator-supplied init scripts (instrumentation hooks, console capture, fingerprint shims) on patchright is forced to choose one or the other. Our project (browser-driver MCP) had to ship a workaround that drops operator init_scripts with a warning on patchright/cloak engines, and skip our own JS-layer Sec-CH-UA
navigator.userAgentDataoverride on those engines too, both because the workaround applies uniformly to everycontext.add_init_scriptcall post-launch.Suspected area
Looks like a race between patchright's own stealth init scripts being registered on the persistent context's default page and any subsequent
add_init_scriptcall mutating the context's init-script set while the page's network stack is still settling. The persistent context returns with the default page already open, which is what makes this different from the plain Playwright launch + new_context pattern.Happy to test a patch; we can validate the fix against our smoke-test session against httpbin and against a Cloudflare-fronted target.