2025, Dec 10 09:00

Troubleshooting nodriver 0.46.1 with Chrome 136 on Fedora 42: preventing early exits and verifying Cloudflare

Learn how to stop nodriver 0.46.1 with Chrome 136 on Fedora 42 from exiting on launch: stable flags and verify_cf guidance for Cloudflare-protected pages.

When nodriver 0.46.1 is paired with Google Chrome 136 on Fedora 42, a session may exit immediately after launch. The window flashes, the process cleans up a temporary profile, and there is no time for the target page to load. This can be especially visible when trying to reach Cloudflare-protected routes and calling verify_cf(). The same abrupt shutdown can appear on other sites as well, which makes troubleshooting less obvious.

Minimal example that triggers the behavior

The snippet below reproduces the early exit: a browser starts, a page is requested, verify_cf() is awaited, and the script ends almost instantly.

import nodriver as drv
from time import sleep
async def entry():
    agent = await drv.start(headless=False)
    tab = await agent.get('https://dash.cloudflare.com/login')
    await tab.verify_cf()
if __name__ == '__main__':
    drv.loop().run_until_complete(entry())
    sleep(2)

What actually happens

From the outside, it looks like a lifecycle issue rather than a navigation error: the page does not get a chance to render, and the only trace in logging is that nodriver successfully removed a temporary profile in /tmp. Using asyncio.run() instead of the library loop does not change the outcome. The effect is the same across multiple destinations, not only Cloudflare-protected ones.

Separately, when verify_cf() does run, it may click the Cloudflare checkbox but the embedded box can still ask for another click after a short loading phase. Performing that click manually in the same session allows the challenge to pass.

A pragmatic way to keep the session alive and observable

Starting the browser with a more explicit configuration helps keep it running long enough to observe what is happening and to execute verify_cf(). The adjustments below include disabling sandboxing and a set of flags that reduce system-level contention, along with lightweight tracing and explicit teardown. This does not change the core logic of your flow, but it gives the session stability and visibility.

import nodriver as drv
import asyncio
from time import sleep
async def runner():
    try:
        session = await drv.start(
            headless=False,
            sandbox=False,
            user_data_dir=None,
            args=[
                '--no-sandbox',
                '--disable-dev-shm-usage',
                '--disable-gpu',
                '--disable-features=VizDisplayCompositor',
                '--disable-extensions',
                '--disable-plugins',
                '--disable-images'
            ]
        )
        print('Browser session initialized')
        view = await session.get('https://dash.cloudflare.com/login')
        print('Navigation completed')
        await asyncio.sleep(3)
        await view.verify_cf()
        print('verify_cf() invoked')
        await asyncio.sleep(10)
    except Exception as exc:
        print(f'Error: {exc}')
        import traceback
        traceback.print_exc()
    finally:
        try:
            await session.quit()
        except:
            pass
if __name__ == '__main__':
    asyncio.run(runner())

With this setup, the browser starts reliably, navigates to the target page, and verify_cf() is executed. If the Cloudflare box asks for repeated interaction afterward, performing it manually within the same instance can let the check succeed.

Why this matters

Automated browsing stacks are brittle across browser versions and OS builds, and premature shutdowns can be misread as navigation or script errors. A configuration that keeps the session in a stable, observable state is essential for diagnosing where the failure actually occurs. In this case, ensuring the process stays alive surfaces the distinction between process lifetime, page load timing, and the behavior of verify_cf() within a challenge widget.

Takeaways

Keep the launch parameters explicit so the session does not end before your async tasks run. Add minimal tracing to understand where the control flow reaches and where it stops. If verify_cf() triggers the challenge but the widget still requests another click, interacting manually in the same session can allow the flow to proceed. Also make sure your Python indentation and async structure are clean, so execution reaches the awaits you expect.

The bottom line is simple: stabilize the browser lifecycle first, then observe the Cloudflare step. Once the session consistently starts, you can see what verify_cf() accomplishes and where manual interaction is still required.