Fix "Address already in use" on port 9222 after a crashed headless Chrome
The problem
After an OOM-killed headless session, every relaunch failed. Puppeteer: Failed to launch the browser process! bind() returned error: Address already in use. Playwright: browserType.launch: Browser closed unexpectedly with a missing DevToolsActivePort file. lsof showed orphaned processes still holding the debugging socket.
What didn't work
pkill chrome— the headless binary is oftenheadless_shell, andchrome_crashpad_handlerkeeps running regardless, so the port stays bound.- Restarting the Node worker — the orphans are detached from their parent and survive it.
- Picking a new port each launch — the zombie still leaks memory and file descriptors until the box falls over.
The fix
# see exactly who holds the port
lsof -nP -iTCP:9222 -sTCP:LISTEN
# kill every browser artifact the launcher could have left — match by binary/args, not by port:
pkill -f 'headless_shell' || true
pkill -f 'chrome_crashpad_handler' || true
pkill -f 'chrome.*--remote-debugging-port=9222' || true
// and make the launcher clean up after itself so the next crash doesn't orphan another one
const { chromium } = require('playwright');
const browser = await chromium.launch({ headless: true });
let exitCode = 0;
try {
// ... scrape / automate
} catch (err) {
console.error(err);
exitCode = 1;
} finally {
await browser.close().catch(() => {}); // never leave the browser to be orphaned
}
process.exit(exitCode);
Why it works
A crashed session leaves the browser process alive and still bound to the debugging port; matching by binary name and by the --remote-debugging-port argument catches the zombies a bare pkill chrome misses, and the finally close removes the source of new orphans.