Changelog
September 27, 2026 ยท View on GitHub
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Fixed
Added
Changed
Removed
[0.17.0] - 2026-09-26
This is quite a large release, including a number of fixes from first-time contributors (thank you!) as well as ports for all nodriver features and bugfixes that have been added upstream since Zendriver was forked. Additionally, I've managed to remove the last remaining # type: ignore comments, which uncovered a few additional bugs that have also been fixed in this release.
This release marks two years of Zendriver! Huge thanks to everyone that has contributed so far.
Fixed
- Fix
ValueErroron startup when thelangoption is set @bercedev - Fix
verify_cf()never clicking the Turnstile checkbox on Turnstile markup where thecf-turnstile-response/cf_challenge_responseinput is rendered as a document-level sibling of the challenge's shadow-DOM host rather than nested inside it. The lookup was scoped tohost_element, always found nothing, andverify_cf()returned before ever callingmouse_click. Now queries the input from the document (tab.query_selector) instead. @A-Nolan - Fix HTTP method casing to resolve CDP connection issues on some Windows installations @fitudao3788
- Fix
404 Not FoundWebSocket connection error foriframetargets by routing all target connections to/devtools/page/<target_id>inBrowser._handle_target_update. - Fix
Browser.stop()always waiting 3 seconds and then force-killing the browser process, even when the browser had already exited gracefully. The exit check readPopen.returncode, which is only updated bypoll()/wait(). @stephanlensky - Fix
Browser.start()always sleeping forbrowser_connection_timeoutbefore the first connection attempt. It now tries to connect immediately and only waits between retries. @stephanlensky - Fix
Browserinstances never being garbage collected afterBrowser.stop(). The asyncio atexit callback and the global registered instances set kept a reference to every browser, and per-target connections were left open. @stephanlensky - Fix the connection listener crashing with
InvalidStateErrorwhen a response arrived for a command that was already cancelled (for example byasyncio.wait_for), which left later commands hanging forever @stephanlensky - Fix every received CDP event being kept in memory for the lifetime of its connection @stephanlensky
- Fix
Tab.find()raisingProtocolException: No search session with given id foundinstead of retrying when the page's document is replaced during the search @stephanlensky - Fix
await tabraisingValueError: No listener created yeton a tab which no commands were sent to yet, such as one just opened withbrowser.get(url, new_tab=True)@stephanlensky - Fix
browser.get(url, new_tab=True)intermittently raisingStopIterationwhen the new tab was not registered yet, andbrowser.get()returning before the navigation when another target changed in the meantime @stephanlensky - Fix Widevine DRM being unavailable on platforms where Chrome installs it through the component updater, such as Windows. Removed the
--disable-component-update,--disable-background-networking,--disable-backgrounding-occluded-windowsand--disable-renderer-backgroundingdefault browser arguments @stephanlensky - Fix
Browser.start()sometimes returning before the browser's initial tab was registered, which madebrowser.get(url)raiseRuntimeError: coroutine raised StopIteration.browser.get(url)now raises a descriptive error if there is no tab to navigate @stephanlensky - Fix extensions added with
Config.add_extension()not loading on Chrome 137+. The default--disable-featuresswitch was overridden by a second one, since Chrome only uses the last. All--disable-featuresswitches, including user-provided ones, are now merged into one, and--enable-unsafe-extension-debuggingis passed when extensions are added @stephanlensky - Fix commands hanging forever when the browser connection closed while they were waiting for a response, for example because the browser crashed or
Connection.aclose()was called. Pending commands now raise instead, andConnection.send()raisesProtocolExceptioninstead of returningNonewhen the connection is closed @stephanlensky - Fix mouse helpers dispatching inconsistent button and pressure state that pages can observe. Mouse clicks and drags now report a pressure of
0.5while a button is held andbuttons=0on release, drag movements keepbuttons=1,mouse_move()no longer sends amouseReleasedevent, andTab.mouse_move()withstepsnow moves from the last mouse position instead of from(0, 0). Thebuttonsargument ofmouse_click()is deprecated and ignored @stephanlensky - Fix
add_handler()with a CDP domain module (for exampletab.add_handler(cdp.network, handler)) registering the domain's enum types instead of its events, so the handler was never called @stephanlensky - Fix synchronous event handlers sometimes calling only the last registered handler when several handlers were registered for the same event @stephanlensky
- Fix event handlers being called a second time with only the event when they raised a
TypeError. Handlers now receive the connection as a second argument only if their signature accepts one @stephanlensky - Fix
Element.get()returningNonefor attributes with an empty value, such asdisabled="". It now returns""and only returnsNonewhen the attribute does not exist @stephanlensky - Fix
remove_handlers(event_type)raisingKeyErrorwhen no handlers were registered for that event @stephanlensky - Fix
Element.get_position(abs=True)raisingAttributeError@stephanlensky - Fix
Element.get()returning dictionary methods for attribute names such asitemsorkeysinstead ofNone@stephanlensky - Fix
Tab.find_elements_by_text()skipping matches which are not part of the fetched document tree @stephanlensky - Fix
CookieJar.get_all(requests_cookie_format=True)raisingImportErrorwhenrequestsis not installed, and returning session cookies as already expired. It now buildshttp.cookiejar.Cookieobjects with the standard library @stephanlensky
Added
- Add support for unstable variants of Microsoft Edge on macOS @mokurin000
- Add
Tab.get_frames()andElement.get_frame()to access out-of-process (e.g. cross-origin) iframes asTabobjects, and aninclude_framesoption tofind(),find_all(),select()andselect_all()which also searches these iframes @stephanlensky - Add
Browser.create_context()to open a tab in a new browser context with its own proxy. Authenticated HTTP, HTTPS and SOCKS5 proxies (e.g.socks5://user:pass@host:port) are supported through a local forwarding proxy @stephanlensky - Add
Tab.mouse_down(),Tab.mouse_up()andTab.mouse_drag()to press, hold and release mouse buttons separately, for example to drag a slider with custom movement @stephanlensky - Add
Browser.get_tab()to find a tab by text in its URL or title @stephanlensky - Add
Element.shadow_childrento access the children of an element's open or closed shadow root @stephanlensky - Add
Tab.is_scrolled_to_bottom()andTab.bypass_insecure_connection_warning()@stephanlensky
Changed
- Tabs and other targets now share the browser's websocket connection using flat CDP sessions (
Target.attachToTargetwithflatten=True) instead of opening a separate websocket per target. This fixesHTTP 500errors when connecting to iframe targets. @stephanlensky Tab.set_download_path()now creates the download directory if it does not exist @stephanlensky- Expert mode no longer passes
--disable-web-security@stephanlensky
[0.16.0] - 2026-08-16
Fixed
- Fix
patternargument inCookieJar.save@Lyesbcb
Added
- Add support for Microsoft Edge browser @mokurin000
[0.15.5] - 2026-07-15
Fixed
- Fix
SyntaxErroron Python 3.14 inConnection._register_handlers. PEP 765 makescontinue/break/returnthat exits afinallyblock aSyntaxError(was aSyntaxWarningin 3.13), sozendriver/core/connection.pyfailed to import under 3.14. Removed the no-opfinally: continue(the loop already falls through to the next iteration, and the enclosing bareexcept:catches everything, so nothing changed behaviorally). Fixes #252
[0.15.4] - 2026-06-27
Fixed
- Fix
query_selector_allandselect_all(include_frames=True)not searching nested iframes. CDP'squerySelectorAllonly queries within a single document boundary, so elements inside nested iframes were never found. Now walks the full DOM tree to collect each iframe'scontent_documentand queries them individually. Also adds a guard for cross-origin iframes wherecontent_documentisNone. @chronoAP - Fix
Connection._register_handlersreenabling already manually enabled domains @S-Tarr - Fix flakey behavior in
api-reponses-tutorial-2tutorial @S-Tarr
Changed
- Updated CDP models @nathanfallet
[0.15.3] - 2026-03-11
Fixed
- Fix crash in
browser.cookies.get_all()on Chrome 146 when thesamePartyfield is missing in the cookie JSON returned by CDP. - Fix
clear_input()andclear_input_by_deleting()silently failing on React controlled inputs due to_valueTrackermechanism; also fix infinite loop inclear_input_by_deleting()on some VM environments caused byVK_DELETEbeing treated as backward-delete. @nathanfallet
Changed
- Docs:
coremodules included in documentation reference section. Slight modifications were done, mostly to remove warnings. @Kajmany
[0.15.2] - 2025-11-29
Fixed
- Fix race condition in
Tab.query_selectorandTab.query_selector_allon stale document. @Avejack
[0.15.1] - 2025-11-18
Fixed
- Fix
NameErrorinTab.query_selectorandTab.query_selector_alldue to type-checking import ofElement. @Avejack
[0.15.0] - 2025-11-04
Fixed
- Fix
browser.stoppedto detect when the browser was closed by the user. @puc9 - Fix typo in
Browser._handle_target_updatethat prevented target_info updates @puc9 - Fix #128: TimeoutError caused by Tab.xpath @ionutcatana
- Fix WebRTC IP Leaks @ethcipher
Added
- Add
disable_webrtcanddisable_webglargs toConfigclass @ethcipher
Changed
- Fix
.dockerignore@tomokinakamaru
[0.14.2] - 2025-09-09
Fixed
- Fix
evaluateto return the correct value instead of None for falsy values @thromer
[0.14.1] - 2025-09-07
Fixed
- Fix calls to
evaluatethat return JSON: only setserialization_optionswhenreturn_by_value=False@thromer - Fix #184:
download_patherror inDownloadExpectation@MadsHangaard
[0.14.0] - 2025-08-28
Fixed
- Add nodriver evaluate serialization options for improved JavaScript evaluation @dbrun3
Added
- Allow reset expect and intercept @nathanfallet
[0.13.1] - 2025-07-27
Fixed
- Remove unnecessary usage of
typing_extensions(fixesNo module named 'typing_extensions'error) @stephanlensky - Fix verify_cf() @H1steria
- Fix relative coordinates of flash_point() @H1steria
Added
- Add flash_point() to Tab.mouse_click() @H1steria
Changed
- Remove Connection
__getattr__and replace it with declared@property@nathanfallet - Deprecate Element
__getattr__and addget(name: str)method @nathanfallet
[0.13.0] - 2025-07-24
Fixed
- Gracefully close the browser process on exit @nathanfallet
Added
- Add support for Brave browser @H1steria
[0.12.1] - 2025-07-17
Fixed
- Remove usage of StrEnum to fix compatibility with Python 3.10 @stephanlensky
[0.12.0] - 2025-07-17
Added
- Added
Tab.intercept@nathanfallet
[0.11.0] - 2025-07-16
Added
- Complete rewrite of keyboard input system with new
KeyEventsclass inzendriver.core.keys@hvgupta- Added support for modifiers (Ctrl, Alt, Shift, Meta)
- Added support for special keys including arrows, escape, delete and backspace
- Added
KeyEvents.from_text()class method for converting plain text to cdp events - Added
KeyEvents.from_mixed_input()class method for handling mixed sequences of text, special keys to cdp events - Proper Handling of shift variants of keys
- Comprehensive key event types:
CHAR,KEY_DOWN,KEY_UP - Added key event type (
DOWN_AND_UP) as a combination ofKEY_DOWNandKEY_UP
Changed
Element.send_keys()now uses the newKeyEventssystem (it is still backwards compatible with passing a string) @hvgupta- Key event processing now properly handles modifier key sequences @hvgupta
- Update CDP schemas @nathanfallet
[0.10.2] - 2025-07-06
Fixed
- Refactor to asynchronous handlers to avoid blocking the listening loop @nathanfallet
[0.10.1] - 2025-07-06
Fixed
- Added
Tab.flash_pointfrom nodriver @nathanfallet - Ensured loading is finished in expect @nathanfallet
[0.10.0] - 2025-07-05
Fixed
- Refactor element waiting loops in
find_*andselect*methods. @H1steria - Improve
query_selectorerror handling to consistently returnNonewhen a node is not found. @H1steria - Add user_agent option to allow bypassing cloudflare javascript challenge in headless mode. @H1steria
- Fixed
langparameter never being set inBrowser.start@nathanfallet
Added
- Added
mouse_moveandmouse_clickmethods from nodriver. @H1steria
[0.9.0] - 2025-07-01
Added
- Added
speedinTab.scroll_downandTab.scroll_upmethods to control the scroll speed @nathanfallet - Allow to wait for promise in
Element.applymethod @nathanfallet - Added
Element.clear_input_by_deletingto handle inputs with custom delete behavior @nathanfallet - Added
Tab.xpathfrom nodriver @nathanfallet
[0.8.1] - 2025-06-07
Fixed
- Add an optional
special_charactersflag toElement.send_keysto support sending special characters (e.g. emojis) @nathanfallet
[0.8.0] - 2025-06-01
Fixed
- Fixed tests so that they can run on Windows (and still run on Linux like before) @nathanfallet
- Remove usage of asyncio subprocess for better compatibility on Windows @nathanfallet
- Added a missing Chrome Canary path for Windows @nathanfallet
- Added a flag to re-enable
--load-extension(disabled by default in Chrome 136+) @nathanfallet
[0.7.1] - 2025-05-08
Changed
- Updated CDP models @jsuarezl
[0.7.0] - 2025-04-28
Added
- Added
Tab.screenshot_b64andElement.screenshot_b64methods to return screenshot as base64 string @falmar - Added
Tab.print_to_pdfto print the current page to a PDF file @stephanlensky
[0.6.1] - 2025-04-25
Fixed
- Fix race condition in
Browser.getandTab.closewhich could cause exceptions, especially when running multiple browsers in parallel @stephanlensky
[0.6.0] - 2025-04-20
Fixed
Browser.getandTab.closewill now wait for their appropiate target events before returning @ccev
Added
- Added
Tab.save_snapshotto export the current page to MHTML format.
[0.5.2] - 2025-04-09
Fixed
- Fixed type annotation of
Element.children@stephanlensky
[0.5.1] - 2025-02-16
Changed
-
Deprecated
zendriver.loop()function. You should instead useasynciofunctions directly, for example:asyncio.run(your_main_method())
[0.5.0] - 2025-02-16
Added
- Add
tab.expect_downloadmethods to wait for download file @3mora2
[0.4.3] - 2025-02-11
Added
- Add logs for Chrome process output on connection failure @stephanlensky
Changed
- Default and launch changed to use
about:blank(faster start and less bandwidth) @raycardillo
[0.4.2] - 2025-02-11
Fixed
- Multiple Browsers can be created without one affecting the other @raycardillo
[0.4.1] - 2025-02-09
Fixed
- Ignore irrelevant
DOM.disableerrors @raycardillo - Test scripts improved for running on macOS @raycardillo
[0.4.0] - 2025-02-06
Added
- Add
tab.expect_requestandtab.expect_responsemethods to wait for a specific request or response @3mora2 - Add
tab.wait_for_ready_statemethod for to wait for page to load @3mora2 - Add
tab.remove_handlersmethod for removing handlers @khamaileon - Clean up temporary profiles when
Browser.stop()is called @barrycarey
[0.3.1] - 2025-01-28
Fixed
- Fixed bug in
find/find_element_by_textwhich causedProtocolExceptionwhen no results were found @stephanlensky
[0.3.0] - 2025-01-25
Fixed
- Added
Tab.set_user_agent()function for programmatically configuring the user-agent, language, and platform @stephanlensky - Improved a few type annotations (
Connection.send()function now returns correctly typed values based on the providedcdp_obj) @stephanlensky
[0.2.3] - 2024-12-14
Fixed
- Fixed mypy linting errors (attempt 2) @stephanlensky
Added
- Handle browser process shutdown on 'Failed to connect to browser' @desoul99
- Added configurable browser connection timeout and tries @desoul99
[0.2.2] - 2024-11-23
Fixed
- Fix
AttributeError: 'tuple' object has no attribute 'value'error inconnection.pywhen using headless browser, @slimshreydy
[0.2.1] - 2024-11-23
Added
- Add automated testing framework! @stephanlensky
- For now, just a few tests are written, including one to test browserscan.com bot detection
- In the future, we can expand this test suite further (see Zendriver#18)
- Add return type annotation to
Tab.get_content()@stephanlensky
Changed
- Upgraded
websocketsto latest version (>=14.0) @yoori @stephanlensky
[0.2.0] - 2024-11-17
Changed
- Updated CDP models @stephanlensky
[0.1.5] - 2024-11-17
Fixed
- Reverted non-functional fixes for mypy linting errors (oops) @stephanlensky
[0.1.4] - 2024-11-17
Fixed
- Fixed a large number of mypy linting errors (should not result in any functional change) @stephanlensky
Added
- Added
zendriver.__version__attribute to get current package version at runtime @stephanlensky
[0.1.3] - 2024-11-12
Added
- Added support for
DOM.scrollableFlagUpdatedexperimental CDP event. @michaellee94
[0.1.2] - 2024-11-11
Fixed
- Pinned requirement
websockets<14, fixing theAttributeError: 'NoneType' object has no attribute 'closed'crash which occurs on the latest version ofwebsockets. @stephanlensky - Fixed incorrect
browser.close()method in examples and documentation -- the correct method isbrowser.stop(). @stephanlensky - Fixed
atexithandler to correctly handle asyncbrowser.stop()method. @stephanlensky
[0.1.1] - 2024-10-29
Added
- Support for Python 3.10 and Python 3.11. All versions >=3.10 are now supported. @stephanlensky
[0.1.0] - 2024-10-20
Initial version, forked from ultrafunkamsterdam/nodriver@1bb6003 with a variety of improvements.
Fixed
Browser.set_allcookies function now correctly uses provided cookies @ilkecan- "successfully removed temp profile" message printed on exit is now only shown only when a profile was actually removed. Message is now logged at debug level instead of printed. @mreiden @stephanlensky
- Fix crash on starting browser in headless mode @ilkecan
- Fix
Browser.stop()method to give the browser instance time to shut down before force killing @stephanlensky - Many
rufflint issues @stephanlensky
Added
- Support for linting with
ruffandmypy. Allrufflints are fixed in the initial release, but manymypyissues remain to be fixed at a later date. @stephanlensky py.typedmarker so importing as a library in other packages no longer causesmypyerrors. @stephanlensky
Changed
- Project is now built with
uv. Automatically install dependencies to a venv withuv sync, run commands from the venv withuv run, and build the project withuv build. See the officialuvdocs for more information. @stephanlensky - Docs migrated from sphinx to mkdocs-material. @stephanlensky
Browser.stop()is now async (so it must beawaited) @stephanlensky
Removed
- Twitter account creation example @stephanlensky