SeleniumHQ/selenium · critical · RuntimeError
CDP devtools module not loaded. Call import_devtools() first
Error message
CDP devtools module not loaded. Call import_devtools() first.
What it means
Raised as a RuntimeError by CdpConnection._handle_event() when the global `devtools` module is None at the time an event message is received from the WebSocket. The devtools module (generated CDP type definitions) must be loaded via import_devtools() before any event can be parsed. The _handle_event method calls devtools.util.parse_json_event(data), so it requires devtools to be initialized. This fires during background event dispatch, not at call time.
Source
Thrown at py/private/cdp.py:314
# Otherwise, continue the generator to parse the JSON result
# into a CDP object.
try:
_ = cmd.send(data["result"])
raise InternalError("The command's generator function did not exit when expected!")
except StopIteration as exit:
return_ = exit.value
self.inflight_result[cmd_id] = return_
event.set()
def _handle_event(self, data: dict):
"""Handle an event.
Args:
data: event as a JSON dictionary
"""
global devtools
if devtools is None:
raise RuntimeError("CDP devtools module not loaded. Call import_devtools() first.")
event = devtools.util.parse_json_event(data)
logger.debug("Received event: %s", event)
to_remove = set()
for sender in self.channels[type(event)]:
try:
sender.send_nowait(event)
except trio.WouldBlock:
logger.error('Unable to send event "%r" due to full channel %s', event, sender)
except trio.BrokenResourceError:
to_remove.add(sender)
if to_remove:
self.channels[type(event)] -= to_remove
class CdpSession(CdpBase):
"""Contains the state for a CDP session.
Generally you should not instantiate this object yourself; you should callView on GitHub (pinned to aa36b38e69)
Solutions
- Call import_devtools() early in your setup, before creating or using any CDP connection. It is normally called automatically by Connection.create().
- Ensure you are using the standard connection creation path (Connection.create) which calls import_devtools() rather than constructing CdpConnection directly.
- If customizing initialization, call import_devtools() as the first step before any WebSocket communication begins.
Example fix
# before # Connection created without import_devtools() conn = CdpConnection(ws) # ... events arrive, _handle_event raises RuntimeError # after from selenium.webdriver.common.bidi.cdp import import_devtools import_devtools() conn = await Connection.create(url) # standard path # events now parse correctly
Defensive patterns
Strategy: validation
Validate before calling
from selenium.webdriver.common.bidi.cdp import devtools, import_devtools
if devtools is None:
import_devtools() Try / catch
try:
await conn.do_work()
except RuntimeError as e:
if 'devtools module not loaded' in str(e):
import_devtools()
await conn.do_work()
else:
raise Prevention
- Call import_devtools() before any CDP connection setup.
- Use the standard Connection.create() path which handles initialization.
- Verify import_devtools() succeeds without silent failure.
When it happens
Trigger: A CDP event arrives on the WebSocket (dispatched by the reader task to _handle_event) before import_devtools() has been called. The global `devtools` variable is None, so event parsing cannot proceed. This is a race or initialization-order error.
Common situations: Forgetting to call import_devtools() (typically auto-called during connection setup but can be missed in custom setups); a timing issue where events arrive before the devtools import completes; manually constructing a CdpConnection without the standard initialization path.
Related errors
- {fn_name}() must be called in a connection context.
- {fn_name}() must be called in a session context.
- -32700
- CDP version not found in capabilities
- Unable to find url to connect to from capabilities
AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14).
Data as JSON: /api/errors/11f6ce8b0b8ad55c.
Report an issue: GitHub.