How to fix Chrome 136+ blocking CDP on your default profile
Somewhere around Chrome 136, which started rolling out in April 2025, a script that had attached to my own Chrome over CDP for more than a year just stopped working. Same flags, same profile path, same everything. Chrome opened fine when I clicked the icon myself. But the moment Playwright tried to connect with --remote-debugging-port pointed at my real, logged-in profile, the debugging port never came up. No crash, no error dialog. Just a connection refused on 9222.
If you landed here from browser-use’s GitHub issue #1520, this is that exact bug, and it isn’t a bug in browser-use. It’s Chrome doing precisely what it was changed to do. This is for anyone driving their own Chrome profile over CDP, whether that’s an agent framework, a Playwright or Puppeteer script, or a homegrown RPA tool, and who needs the real cookies, saved logins and extensions along for the ride instead of a blank session.
By the end you’ll have a second, automation-only Chrome profile that inherits your real session state, opens with remote debugging on, and never touches the daily-driver browser you read your actual email in. About fifteen minutes, no Chrome downgrade, no registry editing unless you deliberately want the enterprise policy route near the end.
What you need
- Chrome 136 or later installed locally (chrome://version tells you the build). You need to be on the affected version to reproduce this, which you almost certainly already are.
- Roughly 200MB to a couple of GB of free disk, depending on how bloated your current profile’s cache and extension data are.
- A terminal: PowerShell on Windows, Terminal on macOS or Linux.
- Whatever you’re driving Chrome with, Playwright, Puppeteer, Selenium 4’s CDP support, or browser-use.
- curl, or just a second browser tab, to sanity-check the CDP endpoint before wiring up any code.
- Admin rights, only if you end up going the enterprise-policy route in the last step.
Step by step
1. Confirm you’re actually hitting the Chrome 136 block
Open chrome://version and check the build number is 136 or higher. Then try launching Chrome from a terminal with the flags you were already using:
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
--remote-debugging-port=9222 `
--user-data-dir="$env:LOCALAPPDATA\Google\Chrome\User Data"
Expected: Chrome opens as a normal window, but curl http://127.0.0.1:9222/json/version from another terminal gets connection refused, and Chrome may log something to the effect that remote debugging requires a non-default data directory.
If it breaks: if you instead get a clean JSON response back from that curl, you’re not hitting this at all, something else is wrong. Check for a second chrome.exe already squatting on port 9222 with Get-Process chrome.
2. Fully quit Chrome, all of it
Get-Process chrome, chrome_proxy -ErrorAction SilentlyContinue | Stop-Process -Force
Expected: Get-Process chrome returns nothing.
If it breaks: if a chrome.exe process reappears within a second of being killed, Chrome’s “continue running background apps” setting is relaunching it. Turn that off at chrome://settings/system first, then kill it again.
3. Find your real profile directory
chrome://version shows a “Profile Path” field. On Windows it’s typically C:\Users\<you>\AppData\Local\Google\Chrome\User Data\Default. On macOS it’s ~/Library/Application Support/Google/Chrome/Default. On Linux, ~/.config/google-chrome/Default.
Expected: a folder path ending in Default, or Profile 1, Profile 2 and so on if you run multiple Chrome profiles for different accounts.
If it breaks: if you use Chrome’s profile switcher for more than one identity, make sure you note the specific Profile N folder you actually automate with. Copying Default when your real logins live under Profile 2 gets you a clean, logged-out browser and a confusing afternoon.
4. Copy the profile into a new, non-default location
With Chrome fully closed:
New-Item -ItemType Directory -Path "C:\ChromeAutomation" -Force
Copy-Item "$env:LOCALAPPDATA\Google\Chrome\User Data\Local State" -Destination "C:\ChromeAutomation\"
robocopy "$env:LOCALAPPDATA\Google\Chrome\User Data\Default" "C:\ChromeAutomation\Default" /E /XD Cache "Code Cache" GPUCache
The robocopy exclusions skip cache folders, which is most of the bulk and none of the value, cookies and saved logins live in Cookies and Login Data, not in Cache.
Expected: C:\ChromeAutomation now has a Local State file and a Default folder containing Cookies, Login Data, Web Data and Extensions.
If it breaks: robocopy or Copy-Item throws file-in-use errors. That means step 2 didn’t fully take, some Chrome-related process (Crashpad handler, GoogleUpdate) still holds those SQLite files. Check Task Manager for anything with “chrome” or “google” in the name and end it, then retry.
5. Launch Chrome against the copy with debugging on
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
--remote-debugging-port=9222 `
--user-data-dir="C:\ChromeAutomation" `
--profile-directory="Default" `
--no-first-run
Expected: Chrome opens showing your real bookmarks and saved logins, since it booted off the copy. curl http://127.0.0.1:9222/json/version now returns JSON naming your Chrome build.
If it breaks: Chrome opens but CDP still refuses to answer. The single most common cause here is a typo that leaves --user-data-dir pointing back at the original default path. Check it character by character, and confirm nothing else already owns port 9222.
6. Point your automation tool at the new endpoint
Playwright, Python:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp("http://127.0.0.1:9222")
context = browser.contexts[0]
page = context.pages[0] if context.pages else context.new_page()
page.goto("https://example.com")
browser-use, roughly (the exact config field has moved across browser-use releases, check your installed version’s BrowserConfig before copying this blind):
from browser_use import Agent
from browser_use.browser import Browser, BrowserConfig
browser = Browser(config=BrowserConfig(cdp_url="http://127.0.0.1:9222"))
Expected: your script attaches without launching a fresh Chrome of its own, and whatever page is visible in the window you opened in step 5 is what your script sees.
If it breaks: connection refused usually means the Chrome window from step 5 got closed, or the port changed. Logged-out or missing session data means the copy in step 4 happened before Chrome was fully quit, go back to step 2.
7. Verify the session actually carried over
Navigate to a site you’re logged into in your real Chrome and confirm the automated instance shows you as logged in, not a login page. Check chrome://extensions, since some extensions prompt to re-enable “allow in incognito” the first time they run from a new profile path, that’s normal, not a failure.
If it breaks: if you’re logged out, the Cookies file most likely got copied while it was still open and mid-write in the source profile. This is the single most common cause of a copied profile silently losing its login state. Redo steps 2 through 4, and don’t skip the “fully quit” check.
8. Lock the port down, and know the heavier-handed alternative exists
The CDP endpoint has no authentication built in. Chrome’s own DevTools documentation is upfront that anything that can reach the port can fully control the browser, including reading cookies through the Network domain. Bind to 127.0.0.1 (the default), never 0.0.0.0, and don’t put this port behind a public load balancer or a permissive security group.
If your machines are centrally managed, Chrome also documents an enterprise policy, RemoteDebuggingAllowed, that restores the old behavior and lets CDP attach to a default profile again. I don’t use it outside dedicated CI or automation-only machines that never touch a human’s real inbox. Flipping it on a daily-driver install reopens close to the exact cookie-theft path Chrome shipped this change to close in the first place, and that trade isn’t worth the ten minutes it saves.
I haven’t tested any of this against a domain-joined Windows machine with GPO-pushed Chrome policies layered on top, if your org manages Chrome centrally, your mileage may vary and you should check with whoever owns those policies before assuming a profile copy behaves the same way.
Common pitfalls
- Copying the profile while a Chrome-related process is still alive in the background. Task Manager showing zero
chrome.exeisn’t always the full picture, the updater and crash handler count too. - Exposing
--remote-debugging-porton a non-localhost interface. CDP has no auth, so this is handing out full browser control, not just a debugging convenience. - Treating the copy-and-launch as a one-off instead of scripting it. Cookies expire, extensions update, and a manual process you did once by hand breaks silently three weeks later when nobody remembers the exact steps.
- Assuming
RemoteDebuggingAllowedis a free pass back to the old behavior on an everyday machine. It’s meant for managed fleet endpoints that aren’t also someone’s personal inbox. - Pinning Chrome to a pre-136 build to dodge the block entirely. That trades a fifteen-minute fix for an indefinitely unpatched browser, not a good swap.
Scaling this
At 10 profiles, the manual version above is fine. One script, sequential ports from 9222 up, one folder per profile under something like C:\ChromeAutomation\01 through \10.
At 100, you need process supervision, since a crashed Chrome instance now needs to come back on its own rather than you noticing and relaunching it by hand. Disk math that looked trivial at 10 profiles gets real at 100, especially if you skipped the cache exclusions in step 4. And if these 100 profiles are automating 100 different accounts from one host, they’re all leaving from the same IP, which is its own detection problem worth reading up on in how IP reputation is earned and lost. If the accounts specifically need to look like distinct Singapore mobile users with a stable IP for the life of a session, Singapore Mobile Proxy sells exactly that, real Singapore mobile IPs with sticky sessions, wired into each profile through --proxy-server. It’s Singapore-only, so it only fits if your traffic actually needs to be there.
At 1000, you’re not managing folders on one box anymore. That’s a fleet problem: containerized Chrome (our Chrome for Testing vs open-source Chromium comparison is worth reading before you pick a binary), orchestration through a queue instead of a script loop, and profile state synced from object storage per job rather than sitting on a laptop. At this scale, detection and blocking become the dominant engineering problem, not CDP plumbing, and why AI browser agents get blocked is the more relevant read at that point.
Where to go next
Once CDP is reattached and stable, the next failure modes are usually further downstream. Browser agent production checklist covers what else breaks between “works on my machine” and “runs unattended every day.” Observing AI agents: traces, replays, cost is worth it once you have more than one Chrome instance running and need to know which one actually failed and why. And if you’re planning to move this off Windows entirely, does Playwright run on Ubuntu 26.04 LTS yet answers that before you migrate a working setup. More tutorials like this one are on the /blog/ index.
Written by Xavier Fok
disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-09-18.