ScreenshotNeo

BlogHow-to

Fix Python Selenium Screenshots That Are Saved as Empty Files

Trace an empty Selenium screenshot to the capture, browser session, or file write. Check the PNG bytes and output path with a focused Python diagnostic.

By the ScreenshotNeo team4 October 20267 min read

If Selenium saves a zero-byte screenshot, isolate the two stages: getting PNG bytes from the browser and writing those bytes to disk. Inspect the bytes returned by driver.get_screenshot_as_png(), then inspect the absolute output path, file size, and PNG signature. save_screenshot() returning True only indicates that its file operation did not raise an I/O error; it does not validate that the bytes form a usable PNG.

This guide uses Python Selenium and includes runnable diagnostics, a reliable wait pattern, and fixes for common failure branches. Selenium’s screenshot API saves the current window as PNG and recommends a full path and a .png filename. See the Selenium Python API documentation.

1. Check whether Selenium returned PNG bytes

Capture the screenshot as bytes before writing a file. A PNG normally starts with the eight-byte signature \x89PNG\r\n\x1a\n.

from pathlib import Path
from selenium import webdriver

PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"

# Assumes the driver is already configured and on the page you want.
png = driver.get_screenshot_as_png()
print("byte count:", len(png))
print("signature:", png[:8])
print("valid PNG signature:", png.startswith(PNG_SIGNATURE))

output = Path("screenshots/page.png").resolve()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_bytes(png)
print("written:", output)
print("file size:", output.stat().st_size)
print("file signature:", output.read_bytes()[:8])

Interpret the result in layers:

  • Bytes are empty or have no PNG signature: the problem is at or before screenshot acquisition. Record any exception and inspect the active WebDriver session, selected window, browser and driver logs, and versions.
  • Bytes have the PNG signature but the file is empty or different: check the resolved path, permissions, subsequent writes, and whether another process truncates or replaces the file.
  • Bytes and file both have the signature: the file is not empty at the byte level. Check that you are opening the same path and that the image viewer or downstream parser accepts the file.

These checks identify the layer where evidence changes; they do not presume one root cause for every environment.

2. Save to an absolute path and inspect the result

Use this standalone example when you want Selenium’s convenience method. It creates the parent directory, prints the resolved path and checks the file afterward. Start the browser according to your installed Selenium and browser setup.

from pathlib import Path
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By

output = Path("screenshots/page.png").resolve()
output.parent.mkdir(parents=True, exist_ok=True)

options = webdriver.ChromeOptions()
# Add options supported by your installed Chrome/browser environment if needed.
driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    WebDriverWait(driver, 20).until(
        lambda d: d.find_element(By.TAG_NAME, "body").is_displayed()
    )

    ok = driver.save_screenshot(str(output))
    size = output.stat().st_size if output.exists() else None
    signature = output.read_bytes()[:8] if size else b""
    print({
        "saved_return": ok,
        "path": str(output),
        "bytes": size,
        "png_signature": signature == b"\x89PNG\r\n\x1a\n",
    })
finally:
    driver.quit()

Replace the example URL and wait condition with the page and content your job needs. Selenium’s API says save_screenshot() returns False for an I/O error and recommends a full path; its implementation does not validate the PNG payload or dimensions. Therefore, check the file even when the return value is True.

3. Make sure the intended page is ready and selected

A successful navigation call is not proof that a modern page has finished asynchronous rendering. Selenium documents that navigation waits for a page load event, while scripts may continue changing the page afterward. Wait for a meaningful condition in your application before capturing, such as a target becoming visible or a loading marker disappearing.

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

# Wait for the actual content required in the screenshot.
WebDriverWait(driver, 20).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "main article"))
)
# Capture only after that condition is met.
driver.save_screenshot(str(output))

The WebDriver screenshot command captures the current top-level browsing context’s visual viewport. Confirm that the intended tab or window is current and still open. If your test is inside an iframe but intends to screenshot the page window, switch back to the default content first:

driver.switch_to.default_content()
print("current URL:", driver.current_url)
print("window handle:", driver.current_window_handle)
print("open windows:", driver.window_handles)

A closed top-level context is an error. A screenshot is also not a promise that application-specific asynchronous work has completed; the browser’s capture timing cannot replace an explicit wait for your page state.

4. Troubleshoot by symptom

Symptom Likely layer What to check or do
get_screenshot_as_png() raises WebDriver command or browser session Keep the full exception. Check that the session and current window are alive; review Selenium, browser and driver versions and browser/driver logs.
Returned bytes are zero length Capture response Log the exception and session state. Confirm the intended window is selected and inspect browser/driver logs. Do not assume a file permission change will fix missing response bytes.
Returned bytes look valid, file is zero bytes Filesystem or later process Write to a resolved absolute path, create its parent directory, check permissions, and search for later code or another process that truncates or replaces the file.
save_screenshot() returns False File operation Check the path, parent directory, write permissions and I/O error. Use an absolute filename ending in .png.
It returns True, but output is unusable Payload or file inspected Check file size and signature. The return value does not validate PNG contents. Make sure you inspect the same resolved path and that later code did not alter it.
Screenshot shows a blank or stale page Timing or browsing context Wait for the specific content to become visible; confirm URL, current window, and frame context. Navigation’s load event alone may be too early.
Renderer or DevTools disconnection appears Browser/session reliability Capture the full exception, Selenium/browser/driver versions, OS or container, and headless configuration. Treat it as a session failure to investigate, not as proof of a universal headless-mode defect.

A historical Selenium issue reports a Chrome DevTools disconnection during a screenshot attempt. It is an example of a possible renderer/session problem, not evidence that the same cause applies to every empty file. See the Selenium issue report.

5. Use a compact diagnostic checklist

  1. Log the exception, if the capture call raises.
  2. Record len(png) and png[:8] immediately after get_screenshot_as_png().
  3. Write those exact bytes to a resolved path and check the resulting size and first eight bytes.
  4. If using save_screenshot(), record its boolean result, absolute path, extension and file size.
  5. Verify the intended top-level window is open and selected; return to default frame context when appropriate.
  6. Wait for the exact dynamic content needed by the screenshot.
  7. If the command fails at the browser layer, collect Selenium, browser, driver, OS/container and headless details with the logs.

This order separates the browser response from filesystem writing and capture timing without relying on a fixed sleep or assuming a particular browser flag is responsible.

6. Performance, reliability, and cost considerations

For a one-off screenshot, a direct capture is usually the simplest path. In repeated jobs, avoid arbitrary long sleeps: wait for the page condition that matters, set a bounded timeout, and log enough context to diagnose failures. Check the output after capture when a pipeline depends on it. The research evidence does not establish a universal screenshot failure rate, performance benchmark, or a single configuration fix.

Filesystem diagnostics are inexpensive, but repeated retries against a disconnected browser session can waste time and produce misleading empty artifacts. On an exception or invalid payload, retain the diagnostic details and decide whether the job should restart its browser session or fail visibly; do not silently treat a zero-byte file as success.

Or skip the browser setup

If your task is to capture a URL rather than exercise Selenium itself, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF. Its clean-shot flow accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads and cache hits cost nothing, and response headers report the page verdict and billing status.

For Python, install requests if needed, set your API key, and save the response:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

See the ScreenshotNeo API documentation for request options. Its MCP server gives AI agents tools for taking screenshots, getting page information and capturing PDFs. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Try it with a free ScreenshotNeo account.

FAQ

Does save_screenshot() returning True prove the PNG is valid?

No. It indicates the file operation did not report an I/O error. Check the saved file’s size and PNG signature.

Should I always add a fixed sleep before taking the screenshot?

No. Wait for the page element or state your capture depends on, with a timeout appropriate to your application.

Does this command capture an entire long page?

The WebDriver screenshot command described here targets the top-level browsing context’s visual viewport. Do not assume it captures the full document.

What details help diagnose a failure that only happens in CI?

Include the Selenium, browser and driver versions, OS or container, headless configuration, full exception and logs, current window context, byte count, save return value, resolved path and file size.

Sources