Events and debugging
Which page events reach your automation client, how to capture console output anyway, and how to check which renderer actually paints the page.
Console and page-error events
The engine does not forward console or page-error events to automation clients, so page.on("console") and page.on("pageerror") receive nothing. That is by design — forwarding them is exactly what an automation-presence probe measures. In-page window.onerror and unhandledrejection handlers fire normally, so to capture console output collect it in-page and read it back with page.evaluate(). The Python and Node SDKs print a one-time note about this at launch.
Two driver calls depend on those events. Playwright's page.setContent() waits for a console event, so it times out (Puppeteer's works): open the HTML as a data: URL, or write it into the page with page.evaluate(). exposeFunction() and exposeBinding(), in Playwright and Puppeteer, work in the page that is open when you call them, and are gone once that page navigates: call them after the last navigation, or pass results back from page.evaluate().
from urllib.parse import quote
# Instead of page.set_content(html):
# short HTML: a data: URL (the page gets no origin of its own, so no cookies or storage)
page.goto("data:text/html," + quote(html))
# any HTML: write it into a page you have navigated to; it keeps that page's origin
page.evaluate("html => { document.open(); document.write(html); document.close(); }", html)Render-backend coherence check (checkRenderCoherence)
A persona can claim a GPU, but if the page is actually painted by a software rasterizer (SwiftShader / llvmpipe — common headless with no GPU) a strict detector can tell. Probe a live page: it reads the (unmasked) WebGL vendor/renderer the page actually sees, flags a software-rasterizer fallback (a fatal headless tell — enable the canvas bridge or run headed on a real GPU) or an incoherent vendor/renderer pair, and returns a structured verdict. Pass the claimed GPU to also assert the rendered family. Available sync, async, and in Node.
from clearcote import launch, check_render_coherence
browser = launch(fingerprint="seed-123")
page = browser.new_page(); page.goto("about:blank")
verdict = check_render_coherence(page) # {'renderer', 'software_suspected', 'coherent', 'warnings'}
if not verdict["coherent"]:
print(verdict["warnings"]) # e.g. software rasterizer / incoherent GPU family
browser.close()