"What if we spawn Chrome with undetected-chromedriver in a subprocess, then hand it off to the main process via a debug port?" The design looked clean — subprocess handles Chrome initialization and login, main process just picks up the ready session. In theory, it was perfect. After a full day of trying, the conclusion was clear: it’s completely impossible for 4 reasons.
4
Problems hit
1 day
Time wasted
0%
Success rate
Single process
Correct design
1Why Hand Off via Subprocess?
The existing codebase was built on a multiprocess architecture. Each task spawned a subprocess for processing, while the main process acted purely as an orchestrator. Adding Selenium-based tasks into this pattern is where things went wrong.
Chrome is heavy, so the plan was to launch it in a subprocess using--remote-debugging-port, then connect from the main process. The subprocess would handle login, and the main process would receive the authenticated session. But the requirement to use UC (undetected-chromedriver) for anti-bot bypass is where the real problems began.
# Intended design (conclusion: doesn’t work)
# In subprocess:
# UC starts Chrome → login → open debug port
# In main process:
# Connect via regular selenium to debug port
options.add_experimental_option("debuggerAddress", "localhost:9222")
driver = webdriver.Chrome(options=options)2Problem 1: UC Ignores --remote-debugging-port
First wall
UC internally assigns a random port when launching chromedriver. Even if you pass --remote-debugging-port=9222 in options, UC silently overrides it with its own random port.
# What we tried
options = uc.ChromeOptions()
options.add_argument("--remote-debugging-port=9222") # ← ignored
driver = uc.Chrome(options=options)
# Actual port: random (not 9222)Digging into UC’s source code revealed it manages port assignment internally and overwrites any user-specified port. You can technically read UC’s internal port at runtime, but safely passing that value between subprocesses introduces yet another layer of complexity.
3Problem 2: No detach Support
Second wall
When the subprocess exits, Chrome dies with it. Regular chromedriver supports detach=True to keep Chrome alive after the driver process ends. UC does not support this option.
# Works with regular selenium, not with UC
# Regular selenium (works)
options.add_experimental_option("detach", True)
driver = webdriver.Chrome(options=options)
# Chrome survives after process exit
# UC (does NOT work)
options = uc.ChromeOptions()
options.add_experimental_option("detach", True) # ← ignored or errors
driver = uc.Chrome(options=options)
# Chrome dies when driver exitsUC tightly couples the lifecycle of chromedriver and Chrome. This design makes detach fundamentally incompatible. The entire concept of spawning Chrome in a subprocess and handing it off conflicts with UC’s architecture.
4Problem 3: stdout Pipe Inheritance Hell
Third wall — the most baffling one
Running UC inside a subprocess caused the main process’s communicate() to hang forever. UC’s chromedriver inherits the parent process’s stdout pipe and keeps it open. As long as the pipe stays open, communicate() never receives EOF and blocks indefinitely.
# communicate() hangs forever
proc = subprocess.Popen(
["python", "worker.py"], # worker.py runs UC inside
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# This line never returns
stdout, stderr = proc.communicate() # ← infinite wait# close_fds=True workaround — only partial effect
proc = subprocess.Popen(
["python", "worker.py"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True # ← UC still inherits some pipes
)UC’s chromedriver launch mechanism makes it difficult to prevent pipe inheritance. Even if you solve this issue, the previous problems still remain — by this point the limitations were clear.
5Problem 4: Invisible Tabs with Regular chromedriver
Fourth wall
After giving up on UC, we tried launching Chrome with regular chromedriver in the subprocess and connecting from the main process via debug port. The connection succeeded. But the tabs Selenium recognized didn’t match the actual tabs where the work had been done. The logged-in tab was invisible to the main process.
# Tab mismatch after debug port connection
# In main process
options.add_experimental_option("debuggerAddress", f"localhost:{port}")
driver = webdriver.Chrome(options=options)
# Visible tabs
print(driver.window_handles) # ['CDP_unknown_handle']
# The tab opened by subprocess is under a different handle
# Not properly listed in window_handlesThere’s a fundamental mismatch between Selenium’s managed tab context and the actual browser tabs when connecting via debug port. And with regular chromedriver, bot detection triggers anyway — defeating the whole purpose.
6Conclusion: UC Must Run in a Single Process
We abandoned the multiprocess design. UC now runs directly in the main process, and any Selenium work is handled there. The code structure diverged from the original intent, but it runs reliably.
Final Solution
Use UC directly in the main process. Never share or transfer Chrome sessions across subprocesses. If multiprocessing is needed, carve out the Chrome work to run in the main process while delegating everything else to workers.
| Attempt | Result | Reason |
|---|---|---|
| UC + fixed --remote-debugging-port | Failed | UC internally overrides the port |
| UC + detach=True | Failed | UC does not support detach |
| subprocess communicate() wait | Failed | stdout pipe inheritance causes infinite blocking |
| Regular chromedriver + debug port | Failed | Tab context mismatch + bot detection |
| UC directly in main process | Success | Aligns with UC’s intended design |
Key Takeaways
Why UC Subprocess Handoff Doesn’t Work
- ✓UC manages chromedriver internally — port pinning and session transfer are not supported
- ✓No detach support — Chrome dies when the subprocess exits
- ✓UC chromedriver inherits the parent process's stdout pipe, causing communicate() to block forever
- ✓Connecting via debug port with regular chromedriver causes tab context mismatch
- ✓UC is designed to be used in a single process from start to finish
This article is based on hands-on debugging experience from March 2026. Behavior may vary depending on the undetected-chromedriver version. Non-commercial sharing is welcome; for commercial use, please contact us via the contact page.
Need Help Designing Automation Architecture?
Multiprocess automation, Selenium system design, reliable anti-bot bypass — we’ve been through it firsthand and can help you get it right.
Request Automation Consultation