Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

If element.screenshot() does not create an image, first identify which operation failed: Selenium may be holding a stale WebElement, or the screenshot may have succeeded while Python could not write the PNG. Re-find the element after page changes, save to an absolute .png path, check the method’s Boolean return value, and use screenshot_as_png when you need to separate capture from file output. Selenium’s official Python API documents these element methods and their I/O behavior in its WebElement API.

Start with the correct screenshot method

Selenium has two different screenshot scopes:

  • WebElement.screenshot(filename) captures the current element and saves a PNG.
  • driver.get_screenshot_as_file(filename) captures the current browser window, not a crop of the selected element.

The official Selenium documentation describes WebElement.screenshot() as: “Saves a PNG screenshot of the current element to a file.” It recommends a full path and documents a False return when an I/O error occurs. A successful call therefore does not guarantee that the requested relative path was writable; always inspect the return value.

A minimal, reliable element screenshot

This example creates the destination directory, converts the path to an absolute path, waits for the element, and treats a false return as an error.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

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

driver = webdriver.Chrome()
try:
    driver.get("https://example.com")
    element = WebDriverWait(driver, 20).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "h1"))
    )
    saved = element.screenshot(str(output))
    if not saved:
        raise OSError(f"Could not save screenshot to {output}")
    print(f"Saved {output}")
finally:
    driver.quit()

Use a filename ending in .png. The parent directory must exist and the process running the test must have write permission. The explicit absolute path makes it clear where a relative path would otherwise resolve.

Fix a stale element reference

A StaleElementReferenceException is not a PNG-path problem. It means the previously located element no longer represents an element currently present in the page DOM. Navigation, refreshes, JavaScript frameworks replacing a node, and a refreshed frame can all invalidate the handle.

Locate the element only after the page has reached the state you want to capture. If an action changes the DOM, discard the old variable and find the element again:

from selenium.common.exceptions import StaleElementReferenceException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

locator = (By.CSS_SELECTOR, "#invoice")
wait = WebDriverWait(driver, 20)

for attempt in range(3):
    try:
        target = wait.until(lambda d: d.find_element(*locator))
        target.screenshot("screenshots/invoice.png")
        break
    except StaleElementReferenceException:
        if attempt == 2:
            raise
        # The next loop obtains a fresh WebElement from the current DOM.

Do not keep a WebElement across driver.refresh(), navigation, a frame reload, or a UI update that replaces its node. Waiting for presence ensures that a node exists; if the application replaces it immediately, use a wait appropriate to that application’s state and then locate it again.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Separate screenshot capture from disk writing

When screenshot() returns False or no file appears, determine whether the WebDriver command worked by requesting bytes first. Selenium exposes screenshot_as_png and screenshot_as_base64 on the element.

from pathlib import Path

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

png_bytes = element.screenshot_as_png
if not png_bytes:
    raise RuntimeError("The element screenshot returned no PNG bytes")
output.write_bytes(png_bytes)
print(f"Wrote {len(png_bytes)} bytes to {output}")

screenshot_as_png returns PNG bytes, so Python’s own file API controls the write. This isolates WebDriver capture from path, directory, and permission problems. screenshot_as_base64 returns a base64-encoded screenshot when that representation is more convenient:

encoded = element.screenshot_as_base64
print(encoded[:40])

The element still has to be current and valid; changing the output representation does not repair a stale reference.

Interpret the symptom before changing code

Symptom Likely class of problem Action
StaleElementReferenceException The handle refers to a node no longer in the DOM. After navigation, refresh, frame reload, or a framework update, locate the element again and capture the new handle.
The call returns False and no file exists Selenium encountered an I/O error while saving. Use an absolute path, create the parent directory, use a .png name, and verify write permission.
Bytes are returned but direct saving fails Capture works; the destination or file-writing step is failing. Use screenshot_as_png and Path.write_bytes() to handle the write yourself.
A large browser image appears instead of a crop The driver-level API was used. Call element.screenshot() or element.screenshot_as_png for element scope.

Element capture versus a whole-window screenshot

Use the element API when the output should contain only a particular card, chart, form, or heading. Use the driver API when you need the visible browser window, including surrounding UI:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from pathlib import Path

window_file = Path("screenshots/window.png").resolve()
window_file.parent.mkdir(parents=True, exist_ok=True)
saved = driver.get_screenshot_as_file(str(window_file))
if not saved:
    raise OSError(f"Could not save window screenshot to {window_file}")

These methods are not interchangeable. A window screenshot does not become an element crop merely because an element was previously selected.

Check timing, frames, and page state

Wait for the target state

An element can be present before its content, dimensions, or replacement component is ready. Capture after the state your test requires, and then locate the element. If a page update replaces the node between the wait and capture, catch the stale exception and re-locate.

Switch into the correct frame

If the target is inside an iframe, Selenium must be switched into that frame before locating it. If the frame is refreshed, the old element reference becomes stale and the frame context may need to be selected again.

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

frame = WebDriverWait(driver, 20).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "iframe.payment"))
)
driver.switch_to.frame(frame)
target = WebDriverWait(driver, 20).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "form"))
target.screenshot("screenshots/payment-form.png")
driver.switch_to.default_content()

When diagnosing a frame-related failure, confirm that the locator is evaluated in the current browsing context and switch back to the top-level document when finished.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make sure the element has a useful layout

Selenium’s element screenshot captures the current rendered element. A hidden element, an element with no dimensions, or a target covered or replaced during an animation may produce an image that does not match expectations even when the command itself succeeds. Wait for the application’s visible, settled state rather than assuming that presence alone means the visual state is ready.

Common errors and focused fixes

“No such element”

The locator did not match in the current document or frame. Verify the selector, wait for the page state, and switch into the correct iframe before searching.

“Stale element reference”

The DOM node changed after it was found. Re-run the locator immediately before the screenshot; do not retry the same stale object.

False without an exception

The documented failure path is an I/O error while saving. Resolve the absolute destination, create its parent, check permissions and filename, then retry. If uncertainty remains, request screenshot_as_png and write those bytes yourself.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

File appears in an unexpected directory

A relative path is resolved against the process’s current working directory, which may differ between an IDE, a shell, and CI. Print Path.cwd() and use Path(...).resolve() for deterministic output.

Capture succeeds but visual content is incomplete

This is a rendering or timing issue rather than a file-write failure. Wait for the specific content or network-driven state used by your application, then obtain a fresh element reference and capture it.

Build a diagnostic harness

For intermittent failures, log the path, current URL, and exception while preserving a whole-window fallback. The fallback does not replace element capture, but it can show whether the browser reached the expected page.

from pathlib import Path
from selenium.common.exceptions import StaleElementReferenceException

out = Path("artifacts")
out.mkdir(parents=True, exist_ok=True)
print("URL:", driver.current_url)
print("Working directory:", Path.cwd())

try:
    target = driver.find_element(By.CSS_SELECTOR, "#result")
    ok = target.screenshot(str((out / "result.png").resolve()))
    print("element screenshot saved:", ok)
except StaleElementReferenceException:
    print("Target was replaced; locate it again before capture")
except OSError as exc:
    print("File output failed:", exc)
finally:
    driver.get_screenshot_as_file(str((out / "debug-window.png").resolve()))

Keep the browser, driver, Selenium, and operating-system versions with the failure report. The documented API explains the element methods and stale-reference behavior, but it does not establish one universal fix for every browser-driver rendering or platform permission combination.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

For a URL screenshot rather than an in-process Selenium element, ScreenshotNeo provides a website screenshot API. A single GET request returns PNG, JPEG, WebP, or PDF output. 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 disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for all options. The same request in Python:

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)

And in Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, pre-capture clicks, hidden selectors, selector/delay/network-idle waits, request and resource blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, selectable-TTL caching, signed public image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is available on every plan. Sign up free to get 1,000 screenshots a month without a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Performance, reliability, and cost considerations

  • Re-locating an element after a DOM change is safer than retrying a stale object, but avoid unnecessary page reloads that create more replacement opportunities.
  • Writing bytes yourself adds a controllable step and makes path failures obvious; retain the byte count and resolved path in CI logs.
  • Use element screenshots to keep artifacts focused and smaller; use window screenshots when surrounding context is diagnostically important.
  • For URL-based automation, caching can reduce repeated work; ScreenshotNeo lets you choose a cache TTL and reports whether a response was billed.
  • Capture failures are not all equivalent: stale references, unavailable elements, rendering state, and file permissions require different fixes.

FAQ

Does element.screenshot() return image bytes?

No. The file method saves a PNG and returns a Boolean. Use element.screenshot_as_png for bytes or element.screenshot_as_base64 for a base64 representation.

Can I use a JPG filename with Selenium’s element method?

The documented element method saves a PNG and recommends a .png filename. Use PNG for this API.

Why does a screenshot work locally but fail in CI?

Compare the CI working directory, absolute destination, parent-directory creation, and write permissions. Also record browser, driver, Selenium, and operating-system versions because the API documentation does not define one platform-independent rendering workaround.

Frequently Asked Questions

Which Selenium method captures only the selected element?

Use WebElement.screenshot() for a PNG file or WebElement.screenshot_as_png when Python should handle the bytes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should I do immediately after a page refresh?

Locate the element again after the refresh; a WebElement obtained before navigation or DOM replacement may be stale.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.