ScreenshotNeo

BlogEngineering

Why Chrome Headless Exits Immediately with the –repl Flag

Chrome’s --repl example targets the deprecated Headless shell. Learn how to identify the mismatch, use the right binary, and troubleshoot exits.

By the ScreenshotNeo team30 September 20268 min read

Why Chrome Headless Exits Immediately with the --repl Flag

Direct answer: the documented --repl workflow belongs to Chrome’s old Headless shell. Since Chrome 132, that shell is distributed separately as chrome-headless-shell. If you pass --repl to the regular current Chrome executable, the binary and the flag may not match, which is a likely reason the process exits immediately. This is an inference from the official documentation, not a reproduction of every report.

Current Chrome has a unified Headless mode for normal browser automation. The historical interactive REPL is associated with the old shell. First identify the executable and version, then decide whether you need the old REPL at all.

What --repl was designed to do

The old Headless shell documentation describes --repl as a command-line JavaScript prompt. A browser starts, loads a URL, and waits for expressions such as location.href. You can type quit to leave the prompt.

The historical invocation is shaped like this:

chrome --headless --disable-gpu --repl --crash-dumps-dir=./tmp https://www.chromestatus.com/

The documentation also shows a prompt similar to >>>. The --crash-dumps-dir argument is included in that REPL example. Treat the sample as an old Headless shell workflow, not as a guarantee that every modern Chrome binary accepts the same flags.

Why the regular Chrome binary can exit

Chrome now has two relevant distributions:

The executable determines whether the historical REPL workflow is available.
The executable determines whether the historical REPL workflow is available.
Choice Purpose REPL expectation
Current Chrome with unified Headless Browser automation and testing with current Chrome Do not assume the historical --repl interface is available
chrome-headless-shell Standalone old Headless shell behavior Relevant when you specifically need the documented interactive REPL

Chrome for Developers says that Chrome 112 introduced the updated unified Headless implementation. Starting with Chrome 132.0.6793.0, old Headless is available only as the standalone chrome-headless-shell binary. The Chromium Headless README gives the same migration direction.

Therefore, an immediate exit after adding --repl can indicate that your command resolved to regular Chrome rather than the shell. Other causes remain possible: an invalid flag spelling, a wrapper that consumes arguments, a missing display or sandbox permission, a bad URL, or a platform-specific packaging issue. The sources do not establish one universal cause.

Diagnose the executable before changing flags

  1. Save the exact command, including every wrapper and environment variable.
  2. Find the executable your shell resolves.
  3. Print the version from that exact path.
  4. Check whether the file is regular Chrome or chrome-headless-shell.
  5. Run a minimal command without --repl, then add flags one at a time.
command -v google-chrome || true
command -v google-chrome-stable || true
command -v chromium || true
command -v chromium-browser || true
command -v chrome-headless-shell || true

google-chrome --version 2>/dev/null || true
google-chrome-stable --version 2>/dev/null || true
chromium --version 2>/dev/null || true
chrome-headless-shell --version 2>/dev/null || true

On macOS, inspect the application binary directly:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version

On Windows PowerShell, use the resolved command path:

Get-Command chrome, chromium, chrome-headless-shell -ErrorAction SilentlyContinue
& 'C:\Program Files\Google\Chrome\Application\chrome.exe' --version

Do not infer the binary from its package name alone. Containers and CI images often expose a wrapper called google-chrome while installing a different underlying executable.

Use the old REPL workflow when you actually need it

If your goal is the historical JavaScript prompt, use the standalone shell and include a URL plus a writable crash-dump directory:

mkdir -p ./tmp
chrome-headless-shell --headless --disable-gpu --repl --crash-dumps-dir=./tmp https://www.chromestatus.com/

The exact binary location varies by platform and package. Replace chrome-headless-shell with the path reported by your installation. Once the prompt appears, try a small expression:

location.href
quit

This recommendation follows the official old-shell documentation and the Chrome 132 distribution change. It has not been validated against your particular operating system or package.

Use current Headless for automation instead of an interactive shell

For screenshots, tests, scraping, and scripted browser actions, current Chrome Headless is generally the better fit. Drive it through an automation library rather than relying on an undocumented interactive prompt.

Puppeteer example

npm install puppeteer
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({headless: true});
  const page = await browser.newPage();
  await page.goto('https://www.chromestatus.com/', {waitUntil: 'networkidle2'});
  console.log(await page.title());
  await page.screenshot({path: 'shot.png', fullPage: true});
  await browser.close();
})();

Selenium example

pip install selenium
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless=new')
driver = webdriver.Chrome(options=options)
try:
    driver.get('https://www.chromestatus.com/')
    print(driver.title)
    driver.save_screenshot('shot.png')
finally:
    driver.quit()

Current Headless documentation provides automation examples through tools such as Puppeteer and Selenium. The important distinction is that these interfaces manage browser startup, pages, waits, and shutdown for you; they do not depend on the old REPL flag.

Minimal tests that isolate the failure

Run these tests in order and record stdout, stderr, exit status, and the version each time.

  1. Version only: chrome --version. If this fails, fix the installation first.
  2. Headless without REPL: chrome --headless --dump-dom https://example.com. This checks basic startup and navigation.
  3. REPL without a wrapper: invoke the binary by absolute path, not through npm, Docker, or a shell script.
  4. Shell binary: repeat the documented command with chrome-headless-shell.
  5. One flag at a time: remove --disable-gpu, then --crash-dumps-dir, then other custom flags to identify argument-specific behavior.

A successful --dump-dom run proves that ordinary Headless startup works; it does not prove that the REPL is supported by that executable.

Troubleshooting common immediate exits

Symptom Likely cause Fix
Exit code 0 with no prompt Regular Chrome received an old-shell flag, or a wrapper discarded it Print the resolved path; try the standalone shell for the old REPL or use Puppeteer/Selenium
unknown option: repl The executable does not implement that flag Use chrome-headless-shell for the historical workflow
cannot open display The command started a non-headless path or a wrapper removed --headless Pass the supported Headless option and inspect the final argument list
Crash dump directory error The directory does not exist or is not writable Run mkdir -p ./tmp and choose a writable absolute path
Browser starts, then closes after navigation URL load failure, policy, certificate, or process supervisor termination Try https://example.com, capture stderr, and test outside the supervisor
Works locally, exits in CI Sandbox, missing libraries, user permissions, or a different Chrome package Compare versions and paths; install required dependencies; change sandbox settings only according to your CI security policy
Prompt appears and input is ignored stdin is not attached, or a wrapper redirected it Run directly in an interactive terminal with stdin preserved

Do not add random flags until the command works in its smallest form. Every additional switch makes it harder to tell whether the failure is caused by Chrome, the shell, a wrapper, or the operating environment.

Version and packaging edge cases

Chrome versus Chromium

Chrome, Chromium, and vendor packages can expose different executable names and build options. Always use the version command on the exact binary that will run in production. A system may contain several versions at once.

Wrappers and process managers

npm scripts, Python subprocess helpers, Docker entrypoints, and CI actions can alter quoting or consume arguments beginning with dashes. Log the final argument array where possible. In a shell, use an absolute path and quote URLs containing query strings.

Remote debugging is a different interface

If you need to inspect a running browser programmatically, use a supported automation or DevTools connection. The old REPL is not a replacement for a stable automation protocol.

Performance, reliability, and cost considerations

The standalone shell may be useful for a narrow legacy workflow, but maintaining browser binaries, fonts, sandbox permissions, network access, and crash handling is part of your application. Current automation libraries add setup overhead while giving you explicit lifecycle control, waits, retries, and page APIs.

For repeated screenshots, reuse a browser process when your isolation requirements permit it, create a fresh page per job, set navigation and operation timeouts, and close pages after failures. Avoid retrying a deterministic unsupported flag; retries help transient navigation failures, not an executable mismatch. Pin a browser version in CI and log its version with every capture so upgrades are diagnosable.

Browser screenshots also consume CPU, memory, and network bandwidth. Full-page captures and pages with many lazy-loaded images take longer than viewport shots. Cache outputs when the source has not changed, and cap concurrency to avoid resource exhaustion.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you need an image or PDF without maintaining Chrome binaries. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Cookie and consent banners are accepted before capture, and more than 60 known consent platforms, newsletter popups, and chat widgets can be removed. Each step can be turned off.

A managed screenshot service can handle browser setup and cleanup before returning the image.
A managed screenshot service can handle browser setup and cleanup before returning the image.

Only clean shots are billed. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for all options. A minimal request looks like this:

curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'}, timeout=90)
open('shot.webp', 'wb').write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Options include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets, custom viewports, retina scale, PDF paper settings and page ranges, custom CSS and JavaScript, clicks, selector or network-idle waits, blocked ads and trackers, custom headers and cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of 100 URLs per call, usage data, and an OpenAPI specification.

There is a free plan with 1,000 screenshots per month and no card. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free. Create a free ScreenshotNeo account.

FAQ

Is --repl removed from all Chrome builds?

The official material documents it for old Headless shell behavior. It does not promise support in every current Chrome executable.

What changed in Chrome 132?

From Chrome 132.0.6793.0, old Headless became a standalone chrome-headless-shell binary rather than part of the regular Chrome binary.

Should I downgrade Chrome?

Usually no. Use the standalone shell only when you need the legacy REPL; otherwise use current Headless automation.

Can an immediate exit be caused by something else?

Yes. The exact command, operating system, executable path, version, wrapper, permissions, and stderr are required to establish an individual cause.

How do I capture a page without installing Chrome?

Use ScreenshotNeo’s API or MCP server. It handles browser setup and reports whether a response was clean and billable.

Primary sources