Skip to content

launch_persistent_context: any post-launch context.add_init_script call breaks DNS resolution on first goto (ERR_NAME_NOT_RESOLVED) #113

@poodle64

Description

@poodle64

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions