-
Notifications
You must be signed in to change notification settings - Fork 1.4k
File descriptors leak in undetected (UC) mode #3679
-
Hi,
I’m seeing a steady increase in open file descriptors whenever I activate UC mode (uc=True), leading eventually to:
filedescriptor out of range in select()
When UC mode is off the FD count remains flat.
When I use uc_open_with_reconnect and force a gc.collect() after teardown, the leak stops.
Below are three minimal repros—please let me know what else I can provide.
CDP
This script repeatedly opens a browser, calls activate_cdp_mode(...), then quits. You can see the number of FDs jump by ~8–10 each time, and connections in CLOSE_WAIT remain.
import os import time import psutil from seleniumbase import SB def _fd_exists(fd: int) -> bool: try: os.fstat(fd) return True except OSError: return False def inspect_fds(): p = psutil.Process() print(f"Count FD (psutil): {p.num_fds()}") connections = p.net_connections() if connections: print("Open connections:") for conn in connections: print(f" {conn}") if __name__ == "__main__": print("Start") inspect_fds() print('Browser Running') for i in range(1, 4): print(f"\nIteration {i}") with SB(uc=True) as browser: browser.activate_cdp_mode("https://example.com") browser.sleep(1) time.sleep(1) inspect_fds()
Output:
Start
Count FD (psutil): 3
Browser Running
Iteration 1
Count FD (psutil): 11
Open connections:
pconn(fd=5, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51824), raddr=addr(ip='::1', port=51681), status='CLOSE_WAIT')
pconn(fd=6, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51743), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=7, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51750), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
Iteration 2
Count FD (psutil): 17
Open connections:
pconn(fd=5, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51824), raddr=addr(ip='::1', port=51681), status='CLOSE_WAIT')
pconn(fd=6, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51743), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=7, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51750), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=9, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51968), raddr=addr(ip='::1', port=51837), status='CLOSE_WAIT')
pconn(fd=17, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51890), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=18, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51891), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
Iteration 3
Count FD (psutil): 26
Open connections:
pconn(fd=5, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51824), raddr=addr(ip='::1', port=51681), status='CLOSE_WAIT')
pconn(fd=6, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51743), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=7, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51750), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=9, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51968), raddr=addr(ip='::1', port=51837), status='CLOSE_WAIT')
pconn(fd=16, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=52119), raddr=addr(ip='::1', port=51976), status='CLOSE_WAIT')
pconn(fd=17, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51890), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=18, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=51891), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=27, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=52043), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=28, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=52044), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
UC without GC
Here we use uc_open_with_reconnect(...) instead of CDP. The leak still occurs, growing by ~1 FD per iteration.
import os import time import psutil from seleniumbase import SB def _fd_exists(fd: int) -> bool: try: os.fstat(fd) return True except OSError: return False def inspect_fds(): p = psutil.Process() print(f"Count FD (psutil): {p.num_fds()}") connections = p.net_connections() if connections: print("Open connections:") for conn in connections: print(f" {conn}") if __name__ == "__main__": print("Start") inspect_fds() print('Browser Running') for i in range(1, 10): print(f"\nIteration {i}") with SB(uc=True) as browser: browser.uc_open_with_reconnect("https://example.com", 4) time.sleep(1) inspect_fds()
Output:
Start
Count FD (psutil): 3
Browser Running
Iteration 1
Count FD (psutil): 9
Open connections:
pconn(fd=3, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=50953), raddr=addr(ip='::1', port=50847), status='CLOSE_WAIT')
Iteration 2
Count FD (psutil): 10
Open connections:
pconn(fd=3, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=50953), raddr=addr(ip='::1', port=50847), status='CLOSE_WAIT')
pconn(fd=5, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51075), raddr=addr(ip='::1', port=50976), status='CLOSE_WAIT')
Iteration 3
Count FD (psutil): 11
Open connections:
pconn(fd=3, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=50953), raddr=addr(ip='::1', port=50847), status='CLOSE_WAIT')
pconn(fd=5, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51075), raddr=addr(ip='::1', port=50976), status='CLOSE_WAIT')
pconn(fd=6, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=51192), raddr=addr(ip='::1', port=51090), status='CLOSE_WAIT')
UC using GC
Adding a manual garbage collection after each teardown appears to reclaim all sockets—FD count stays constant. But it only works for uc_open_with_reconnect.
import gc import os import time import psutil from seleniumbase import SB def _fd_exists(fd: int) -> bool: try: os.fstat(fd) return True except OSError: return False def inspect_fds(): p = psutil.Process() print(f"Count FD (psutil): {p.num_fds()}") connections = p.net_connections() if connections: print("Open connections:") for conn in connections: print(f" {conn}") if __name__ == "__main__": print("Start") inspect_fds() print('Browser Running') for i in range(1, 4): print(f"\nIteration {i}") with SB(uc=True) as browser: browser.uc_open_with_reconnect("https://example.com", 4) time.sleep(1) gc.collect() inspect_fds()
Output:
Start
Count FD (psutil): 3
Browser Running
Iteration 1
Count FD (psutil): 6
Open connections:
pconn(fd=3, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=49683), raddr=addr(ip='::1', port=49619), status='CLOSE_WAIT')
Iteration 2
Count FD (psutil): 6
Open connections:
pconn(fd=5, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=49759), raddr=addr(ip='::1', port=49690), status='CLOSE_WAIT')
Iteration 3
Count FD (psutil): 6
Open connections:
pconn(fd=3, family=<AddressFamily.AF_INET6: 30>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::1', port=49996), raddr=addr(ip='::1', port=49776), status='CLOSE_WAIT')
Environment:
SeleniumBase version: e.g. 4.37.2
Python version: e.g. 3.13
OS: MacOS and Linux
Chrome: 135.0.7049.85
Chromedriver: matching Chrome
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 7 comments 14 replies
-
Do you know how to find specifically the code where the file descriptors leaks occur? The specific locations would be helpful. Much of UC Mode / CDP Mode is based on undetected-chromedriver
/ nodriver
, which had several memory leaks: https://github.com/search?q=repo%3Aultrafunkamsterdam%2Fundetected-chromedriver+%22memory+leak%22&type=issues
Beta Was this translation helpful? Give feedback.
All reactions
-
Hello @mdmintz,
Thank you for your response. I spent several hours debugging and have identified a number of issues:
1. Session-based requests
I replaced all calls to requests.get with a with requests.Session() as session: context manager in both init.py and cdp.py.
Original
SeleniumBase/seleniumbase/undetected/cdp.py
Line 104 in b9a89f7
Updated
You can review all of my changes here:
master...felipehertzer:SeleniumBase:fd-leak
With these adjustments, the leak in uc_open_with_reconnect no longer occurs (even without invoking the garbage collector), and the number of leaked file descriptors in activate_cdp_mode is greatly reduced.
2. Remaining leak in activate_cdp_mode
Despite the improvements above, two file descriptors (FD 6 and FD 7) still remain open when invoking activate_cdp_mode. Both originate from the start and get functions in seleniumbase/undetected/cdp_driver/browser.py:
It appears that each of these functions establishes its own socket connection, which is never closed. Moreover, I have confirmed that the corresponding stop method (at line 569) is never invoked, which I believe is the underlying cause of the remaining leak:
- Diagnostic output
Below is the output demonstrating the two unclosed sockets and their respective call stacks:
FD=6 and FD=7 are the two socks that do not close, and this is the trace of when each is called
[CONNECT] fd=6 → ('127.0.0.1', 9222)
/test//.venv/lib/python3.13/site-packages/seleniumbase/fixtures/base_case.py:4881 in activate_cdp_mode
/test//.venv/lib/python3.13/site-packages/seleniumbase/core/browser_launcher.py:5426 in <lambda>
/test//.venv/lib/python3.13/site-packages/seleniumbase/core/browser_launcher.py:559 in uc_open_with_cdp_mode
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:712 in run_until_complete
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:683 in run_forever
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:2040 in _run_once
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/events.py:89 in _run
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/cdp_util.py:318 in start
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/browser.py:122 in create
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/browser.py:414 in start
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/connection.py:396 in send
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/connection.py:267 in aopen
/test//.venv/lib/python3.13/site-packages/websockets/asyncio/client.py:541 in __await_impl__
/test//.venv/lib/python3.13/site-packages/websockets/asyncio/client.py:467 in create_connection
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:1141 in create_connection
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:1044 in _connect_sock
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/selector_events.py:639 in sock_connect
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/selector_events.py:649 in _sock_connect
/test//tests/test.py:181 in connect
/test//tests/test.py:191 in _print_stack
[NEW SOCKET] fd=7 family=<AddressFamily.AF_INET: 2> type=<SocketKind.SOCK_STREAM: 1>
/test//tests/test.py:205 in <module>
/test//.venv/lib/python3.13/site-packages/seleniumbase/fixtures/base_case.py:4881 in activate_cdp_mode
/test//.venv/lib/python3.13/site-packages/seleniumbase/core/browser_launcher.py:5426 in <lambda>
/test//.venv/lib/python3.13/site-packages/seleniumbase/core/browser_launcher.py:607 in uc_open_with_cdp_mode
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:712 in run_until_complete
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:683 in run_forever
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:2040 in _run_once
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/events.py:89 in _run
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/browser.py:294 in get
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/connection.py:396 in send
/test//.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/connection.py:267 in aopen
/test//.venv/lib/python3.13/site-packages/websockets/asyncio/client.py:541 in __await_impl__
/test//.venv/lib/python3.13/site-packages/websockets/asyncio/client.py:467 in create_connection
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:1141 in create_connection
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:1022 in _connect_sock
/test//tests/test.py:175 in __init__
/test//tests/test.py:191 in _print_stack
Beta Was this translation helpful? Give feedback.
All reactions
-
4.37.3
- https://github.com/seleniumbase/SeleniumBase/releases/tag/v4.37.3
(At least UC Mode without CDP Mode no longer has memory leaks. Thank you!)
CDP Mode memory improvements will have to be figured out later.
Beta Was this translation helpful? Give feedback.
All reactions
-
Hey @mdmintz,
Thank you for the update.
After running some additional tests I’ve traced the file‐descriptor leak to the Connection class. Although an aclose() coroutine exists, it’s never invoked. The aexit() method would normally trigger aclose(), but it only executes when Connection is used as an async context manager—which isn’t happening in the current workflow.
To prevent the leak it will need to make sure every connection is explicitly closed, either by:
- Refactoring usage to an async with Connection(...) as conn: pattern, or
- Calling await self.aclose() at the end of send() (and any other method that opens a connection).
Beta Was this translation helpful? Give feedback.
All reactions
-
Fixed in 4.37.4
- https://github.com/seleniumbase/SeleniumBase/releases/tag/v4.37.4
Beta Was this translation helpful? Give feedback.
All reactions
-
Thanks again for your help on this, @felipehertzer!
Beta Was this translation helpful? Give feedback.
All reactions
-
That is great, it seems totally fixed, Thank you.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Hey @mdmintz,
While deploying to production today I noticed that we’re still seeing a connection leak on the CDP side.
It appears to originate around
for i in range(1, 4): print(f"\nIteration {i}") with SB( uc=True, ) as browser: browser.activate_cdp_mode("https://bloomberg.com/news/articles/2024-12-05/barclays-promotes-116-staffers-across-bank-to-managing-director") browser.sleep(5) browser.cdp.get_page_source() inspect_fds()
Iteration 1
Count FD (psutil): 9
Open connections:
pconn(fd=6, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=65386), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
Iteration 2
Count FD (psutil): 13
Open connections:
pconn(fd=6, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=65386), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=19, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=49386), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
Iteration 3
Count FD (psutil): 17
Open connections:
pconn(fd=6, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=65386), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=19, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=49386), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
pconn(fd=23, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=49763), raddr=addr(ip='127.0.0.1', port=9222), status='CLOSE_WAIT')
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/fixtures/base_case.py:4882 in activate_cdp_mode
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/core/browser_launcher.py:5425 in <lambda>
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/core/browser_launcher.py:558 in uc_open_with_cdp_mode
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:712 in run_until_complete
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:683 in run_forever
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:2040 in _run_once
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/events.py:89 in _run
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/cdp_util.py:318 in start
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/browser.py:122 in create
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/browser.py:412 in start
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/connection.py:397 in send
/selenium/.venv/lib/python3.13/site-packages/seleniumbase/undetected/cdp_driver/connection.py:268 in aopen
/selenium/.venv/lib/python3.13/site-packages/websockets/asyncio/client.py:541 in __await_impl__
/selenium/.venv/lib/python3.13/site-packages/websockets/asyncio/client.py:467 in create_connection
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:1141 in create_connection
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:1044 in _connect_sock
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/selector_events.py:639 in sock_connect
/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/selector_events.py:649 in _sock_connect
/selenium/tests/test.py:176 in connect
/selenium/tests/test.py:186 in _print_stack
Beta Was this translation helpful? Give feedback.
All reactions
-
@felipehertzer Did you run on 4.36.7
? I saw no open socket connections with:
import shutil import os import subprocess import psutil from seleniumbase import SB # from seleniumbase import config as sb_config def get_open_fds(): """ Returns the number of open file descriptors for the current process. """ lsof_path = shutil.which("lsof") if lsof_path is None: raise NotImplementedError("lsof is not available on this system.") try: process = subprocess.run( [lsof_path, "-w", "-Ff", "-p", str(os.getpid())], capture_output=True, text=True, check=True, ) output = process.stdout fds = [ line for line in output.split(os.linesep) if line.startswith("f") and line[1:].isdigit() ] return len(fds) except subprocess.CalledProcessError as e: raise RuntimeError(f"Error executing lsof: {e}") from e def inspect_fds(): p = psutil.Process() # print(f"Count FD (psutil): {p.num_fds()}") connections = p.net_connections() if connections: # print("Open connections:") for conn in connections: print(f" {conn}") if __name__ == "__main__": print("Start") inspect_fds() # print('Browser Running') # print(get_open_fds()) for i in range(1, 4): print(f"\nIteration {i}") with SB(uc=True) as browser: browser.activate_cdp_mode("https://example.com") # browser.uc_open_with_reconnect("https://example.com", 1) # print(get_open_fds()) inspect_fds()
If you remove the following line, then the socket connections remain open:
SeleniumBase/seleniumbase/plugins/sb_manager.py
Line 1375 in 94b3fa6
Beta Was this translation helpful? Give feedback.
All reactions
-
Yes, I am using the latest version. Interestingly, the issue only occurs when I add these lines. I not sure if it has anything to do with the time spent to render the website.
browser.sleep(5)
browser.cdp.get_page_source()
Beta Was this translation helpful? Give feedback.
All reactions
-
Not sure, but just sleeping and getting the page source isn't causing any issue:
import shutil import os import subprocess import psutil from seleniumbase import SB # from seleniumbase import config as sb_config def get_open_fds(): """ Returns the number of open file descriptors for the current process. """ lsof_path = shutil.which("lsof") if lsof_path is None: raise NotImplementedError("lsof is not available on this system.") try: process = subprocess.run( [lsof_path, "-w", "-Ff", "-p", str(os.getpid())], capture_output=True, text=True, check=True, ) output = process.stdout fds = [ line for line in output.split(os.linesep) if line.startswith("f") and line[1:].isdigit() ] return len(fds) except subprocess.CalledProcessError as e: raise RuntimeError(f"Error executing lsof: {e}") from e def inspect_fds(): p = psutil.Process() # print(f"Count FD (psutil): {p.num_fds()}") connections = p.net_connections() if connections: # print("Open connections:") for conn in connections: print(f" {conn}") if __name__ == "__main__": print("Start") inspect_fds() # print('Browser Running') # print(get_open_fds()) for i in range(1, 4): print(f"\nIteration {i}") with SB(uc=True) as sb: sb.activate_cdp_mode("https://example.com") sb.sleep(1) source = sb.get_page_source() # sb.uc_open_with_reconnect("https://example.com", 1) # print(get_open_fds()) inspect_fds()
Beta Was this translation helpful? Give feedback.
All reactions
-
Hey @mdmintz,
I apologise for the delayed response. I was only able to replicate this issue with heavy websites as Bloomberg and WSJ, because they use a lot of iframes and background services. I’ve completed my investigation and implemented the following fixes:
-
Tab connections not closing
Complex pages with multiple iframes and background‐sync tasks result in each iframe/background task being treated as a separateTab
instance, and thus opening its own WebSocket connection that was never closed. To address this, I:- Added an
__aexit__
method to theTab
class. - Refactored all
Tab()
instantiations to use the async context manager pattern (async with Tab(...)
), ensuring every connection opened by a tab is properly closed on exit.
- Added an
-
Browser vs. page CDP connections
SeleniumBase’s CDP integration issues two DevTools requests—one for the browser and one for the page—but the previous patch only closed the page connection. I introduced:loop.run_until_complete(driver.cdp_base.connection.aclose())
This explicitly closes the browser connection as well, eliminating the sock‐connection leak.
-
File descriptor leak
Even after fixing the CDP leaks, file descriptors were still accumulating. I discovered that explicitly closing the event loop prevents residual FDs from persisting. Adding:loop.close()
at the end of the sequence stops the FD leaks entirely.
With these changes, both the connection and FD leaks no longer occur in my tests.
Replicate:
Add a print after this line:
print("aopen", self.websocket_url)
And another after this line
print("aclose", self.websocket_url)
Output:
aopen ws://127.0.0.1:9222/devtools/browser/8704fae5-0c58-4cde-9e71-3bc08db5d67c
aopen ws://127.0.0.1:9222/devtools/page/44DEB82983EC2934E27A533AF3B4B211
aclose ws://127.0.0.1:9222/devtools/page/44DEB82983EC2934E27A533AF3B4B211 -- ADDED VIA LAST PATCH
Add this print after this line
print(f"Tab: {websocket_url}")
Output:
Tab: ws://127.0.0.1:9222/devtools/background_page/0795658581062DE1B5C7324A1CE59E1B
Tab: ws://127.0.0.1:9222/devtools/page/BE100F9470F4DFF9EAE6111D063528E9
Tab: ws://127.0.0.1:9222/devtools/iframe/D47CA9CF9CD6A1C7E4B960AA2B6C35D2
Tab: ws://127.0.0.1:9222/devtools/other/28ECCAD15EDE39A2715B6D62F47E3880
Tab: ws://127.0.0.1:9222/devtools/iframe/58B370B21BE44228935BC612331B847A
Tab: ws://127.0.0.1:9222/devtools/iframe/972656F2E27C0D14F8DC873594231CD7
Tab: ws://127.0.0.1:9222/devtools/iframe/A2104C041DE228411DF2D6C480545B35
Tab: ws://127.0.0.1:9222/devtools/iframe/E49C3A9A60EF0E9F51EFE28F0767FC29
Tab: ws://127.0.0.1:9222/devtools/iframe/139D3EFDB3A6682AF0EE518855BCE4CB
Tab: ws://127.0.0.1:9222/devtools/shared_storage_worklet/D2AADED9BDC6A64D9502D3E3CE031659
Tab: ws://127.0.0.1:9222/devtools/iframe/C62A699A188A55AED880137A4FC035AE
Tab: ws://127.0.0.1:9222/devtools/iframe/AD8821505E41C9AF384B995213AFD8B0
Tab: ws://127.0.0.1:9222/devtools/iframe/438D8D778866CC42742858F87EBDC06A
Tab: ws://127.0.0.1:9222/devtools/iframe/782456508D48CE8E79ACE870EA9DCDD5
Tab: ws://127.0.0.1:9222/devtools/service_worker/7E032A9235985AAA3B39ACB5F7D50A40
Tab: ws://127.0.0.1:9222/devtools/iframe/E65FA68B93899660A3CDB600A2A3AF1D
Tab: ws://127.0.0.1:9222/devtools/iframe/207E7ED64B68B047015D5173CC0148EE
Tab: ws://127.0.0.1:9222/devtools/iframe/C45D58802C43A3E2D15E3C5CAC76E360
Tab: ws://127.0.0.1:9222/devtools/worker/D72ADF0B8D1D5F4DC2C228F434ADCE75
Tab: ws://127.0.0.1:9222/devtools/iframe/880DF8BA73E2AE050D4B740F6935BE4E
Tab: ws://127.0.0.1:9222/devtools/iframe/C0A6CA93591AC401A7E5DFEACF4EC6EB
Tab: ws://127.0.0.1:9222/devtools/shared_storage_worklet/A14904B5C51F63FB5155F3314BBF1692
Tab: ws://127.0.0.1:9222/devtools/iframe/9CCE76ACCDD938D148314A1916FAD196
Tab: ws://127.0.0.1:9222/devtools/iframe/0CF877B5E7B8A56BEE8F4B68F6E3ECCB
Tab: ws://127.0.0.1:9222/devtools/iframe/AE28E35914D65A9BE56E9C41F0C18446
Tab: ws://127.0.0.1:9222/devtools/iframe/B38A9B8A798BC9BBC755A060265607FA
Tab: ws://127.0.0.1:9222/devtools/iframe/A6982A83029017435083711C08E9B34E
AFTER THE FIX:
aopen ws://127.0.0.1:9222/devtools/browser/ce99f11f-946b-47a1-acbb-dc3eaee1c710
aclose ws://127.0.0.1:9222/devtools/browser/ce99f11f-946b-47a1-acbb-dc3eaee1c710
aopen ws://127.0.0.1:9222/devtools/page/068CF0E77EC08B808B992D7015CBE11B
aclose ws://127.0.0.1:9222/devtools/page/068CF0E77EC08B808B992D7015CBE11B
My branch: master...felipehertzer:SeleniumBase:master
Beta Was this translation helpful? Give feedback.
All reactions
-
I added a few items into 4.37.8
that didn't cause issues.
(It doesn't resolve the leak, but it's less work from here.)
Beta Was this translation helpful? Give feedback.
All reactions
-
Hey @mdmintz,
Thank you for your input.
I have updated the code again to ensure it checks all tabs, closes their connections, and then closes the browser connection for all drivers
Beta Was this translation helpful? Give feedback.
All reactions
-
Still seeing it with examples/cdp_mode/raw_priceline.py, and at random with other scripts.
Try from a full pull (there are merge conflicts from your branch, as it is behind).
Beta Was this translation helpful? Give feedback.
All reactions
-
Hey @mdmintz,
I have fixed that issue, I have added a close connection when close the tab.
I have run the tests and only those failed:
FAILED examples/swag_labs_user_tests.py::SwagLabsTests::test_swag_labs_user_flows_1_problem_user - AssertionError: Sort Failed! Expecting "Test.allTheThings() T-Shirt" on top!
FAILED examples/test_deferred_asserts.py::DeferredAssertTests::test_deferred_asserts - Exception:
FAILED examples/test_detect_404s.py::BrokenLinkTests::test_link_checking - AssertionError: Broken links detected:
FAILED examples/test_fail.py::FailingTests::test_find_army_of_robots_on_xkcd_desert_island - seleniumbase.common.exceptions.NoSuchElementException: Message:
FAILED examples/test_suite.py::MyTestSuite::test_2 - seleniumbase.common.exceptions.TextNotVisibleException:
FAILED examples/test_suite.py::MyTestSuite::test_4 - seleniumbase.common.exceptions.NoSuchElementException: Message:
FAILED examples/test_todomvc.py::TodoMVC::test_todomvc_0_jquery - seleniumbase.common.exceptions.TextNotVisibleException:
FAILED examples/time_limit_test.py::TimeLimitTests::test_runtime_limit_decorator - seleniumbase.common.exceptions.TimeoutException:
FAILED examples/time_limit_test.py::TimeLimitTests::test_set_time_limit_method - seleniumbase.common.exceptions.TimeLimitExceededException:
FAILED examples/visual_testing/test_layout_fail.py::VisualLayout_FixtureTests::test_python_home_change - seleniumbase.common.exceptions.VisualException:
FAILED examples/visual_testing/test_layout_fail.py::VisualLayoutFailureTests::test_applitools_change - seleniumbase.common.exceptions.VisualException:
FAILED examples/visual_testing/test_layout_fail.py::VisualLayoutFailureTests::test_xkcd_logo_change - seleniumbase.common.exceptions.VisualException:
FAILED examples/youtube_search_test.py::YouTubeSearchTests::test_youtube_autocomplete_results - AssertionError: False is not true : Expected text "seleniumbase" not found in top results! Actual text was "selenium
Beta Was this translation helpful? Give feedback.
All reactions
-
Merged in 4.37.10
- https://github.com/seleniumbase/SeleniumBase/releases/tag/v4.37.10
Thanks for your work on this!
Beta Was this translation helpful? Give feedback.