ScreenshotNeo

BlogHow-to

Selenium Screenshot Captures the Wrong Tab: How to Fix It

Selenium screenshots the current browsing context. Save the original window handle, wait for the intended tab, switch to it, verify it, then capture.

By the ScreenshotNeo team4 October 202610 min read

Direct answer: Selenium captures the page in its current browsing context. When a link or script opens another tab or window, save the original window handle, wait for the new handle, switch to the intended one, verify its URL or title, and only then take the screenshot. A tab looking active on your desktop does not automatically make it Selenium’s selected context.

Selenium uses window handles to address top-level browsing contexts; it does not distinguish tabs from windows. The official Selenium window guide explains that WebDriver does not know which window the operating system considers active, even when a newly opened tab or window receives screen focus.

Why Selenium captures the wrong tab

WebDriver commands act on the session’s current browsing context. Opening another tab creates a new handle, but your code must explicitly switch to it. Calling driver.save_screenshot() before that switch captures the page associated with the old handle.

Window handles are unique identifiers for the session. Their order should not be treated as a reliable way to identify a particular tab. Record the handles before the action and compare them with the handles after it. If multiple tabs could open, select by a known URL or title rather than assuming there is exactly one new handle.

Fix it in Python

This standalone example uses Selenium’s documented test page, which opens a second window from a link. It waits for the new handle, switches to it, waits for the expected title, checks the selected handle, captures the screenshot, and returns to the original window before quitting.

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

TEST_PAGE = "https://www.selenium.dev/selenium/web/window_switching_tests/page_with_frame.html"

options = webdriver.ChromeOptions()
# For a headless run, uncomment the next line:
# options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 10)

try:
    driver.get(TEST_PAGE)
    original = driver.current_window_handle
    handles_before = set(driver.window_handles)

    driver.find_element(By.LINK_TEXT, "Open new window").click()

    # Wait until at least one handle appears that was not present before.
    wait.until(lambda d: bool(set(d.window_handles) - handles_before))
    new_handles = set(driver.window_handles) - handles_before

    # This page is expected to open exactly one new top-level context.
    if len(new_handles) != 1:
        raise RuntimeError(f"Expected one new window, found {len(new_handles)}")
    target = new_handles.pop()

    driver.switch_to.window(target)
    wait.until(EC.title_is("Simple Page"))

    # Check context and page identity before capturing.
    assert driver.current_window_handle == target
    assert driver.title == "Simple Page"
    print("Capturing", driver.current_url, "with title", driver.title)
    driver.save_screenshot("target-tab.png")
finally:
    # Switch only if the original context is still open.
    if original in driver.window_handles:
        driver.switch_to.window(original)
    driver.quit()

The key wait observes the difference between the handles before and after the click. Selenium’s official example also waits for the expected number of windows, identifies the handle that differs from the saved original, switches to it, and verifies the current handle. The set-difference form is safer when the session may already have multiple tabs.

When exactly one new tab is expected

If your test guarantees that it starts with one tab and opens one more, Selenium’s expected condition is concise:

original = driver.current_window_handle
driver.find_element(By.CSS_SELECTOR, "a[target='_blank']").click()
wait.until(EC.number_of_windows_to_be(2))

target = (set(driver.window_handles) - {original}).pop()
driver.switch_to.window(target)
wait.until(lambda d: d.current_url.startswith("https://expected.example/"))
driver.save_screenshot("target.png")

Use this only when two handles is the expected total. If the session can start with more tabs or the action can open several, snapshot the full handle set before the action, wait until the count increases, and choose among the newly added handles using a URL or title condition.

Select the intended tab when several can open

Do not use list(driver.window_handles)[1] as a general selection rule: handle ordering is not a semantic promise that a given index means the desired page. Instead, poll the new handles and inspect each candidate until the page identity matches.

before = set(driver.window_handles)
trigger_action_that_may_open_tabs()

wait.until(lambda d: len(set(d.window_handles) - before) >= 1)
new_handles = set(driver.window_handles) - before

def intended_page_is_open(d):
    for handle in new_handles:
        if handle not in d.window_handles:
            continue
        d.switch_to.window(handle)
        if d.current_url.startswith("https://reports.example/"):
            return handle
    return False

target = wait.until(intended_page_is_open)
driver.switch_to.window(target)
driver.save_screenshot("report.png")

For production test code, handle candidates that are still navigating: a URL or title can be temporarily empty. Keep the predicate focused on a stable marker, such as a URL prefix, a known title, or an element that appears after the page loads. If no candidate matches before the timeout, report the handles and URLs observed so the failure is diagnosable.

Switch, verify, capture, then clean up

  1. Save the current handle before the click or script action that may open a tab.
  2. Record the initial handle set if the session may already contain multiple tabs.
  3. Trigger the action that opens the target context.
  4. Wait for the context to exist. Use an explicit wait for the expected count or a handle set difference; do not use a fixed sleep as the readiness check.
  5. Identify and switch to the target handle. Match a known URL, title, or page element if more than one context is possible.
  6. Verify the page identity and, when useful, wait for a page-specific element before capturing.
  7. Take the screenshot only after the checks pass.
  8. Close or restore deliberately. After closing a non-final tab, switch to a handle that is still open before continuing.

Use driver.save_screenshot("page.png") for the current page screenshot. If the intended output is one element, find it in the selected context and use element.screenshot("element.png"). Selenium documents both page/window and element screenshot capture in its browser window and screenshot documentation.

Other Selenium bindings

The selection rule is the same across language bindings: preserve the original handle, wait until a new one appears, switch, verify, and capture. These short examples show the handle operation in Java and JavaScript. Use your binding’s explicit wait APIs and page-specific assertion for your test.

Java

String original = driver.getWindowHandle();
Set<String> before = new HashSet<>(driver.getWindowHandles());
driver.findElement(By.linkText("Open new window")).click();

new WebDriverWait(driver, Duration.ofSeconds(10)).until(
    d -> d.getWindowHandles().size() > before.size()
);
Set<String> added = new HashSet<>(driver.getWindowHandles());
added.removeAll(before);
if (added.size() != 1) {
    throw new IllegalStateException("Expected one new window, got " + added.size());
}
String target = added.iterator().next();
driver.switchTo().window(target);
new WebDriverWait(driver, Duration.ofSeconds(10)).until(
    d -> "Simple Page".equals(d.getTitle())
);
((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE)
    .renameTo(new File("target-tab.png"));

// If closing target, switch back to the still-open original afterward.
driver.switchTo().window(original);

JavaScript (Node.js)

const { Builder, By, until } = require('selenium-webdriver');

(async function captureTargetTab() {
  const driver = await new Builder().forBrowser('chrome').build();
  try {
    await driver.get('https://www.selenium.dev/selenium/web/window_switching_tests/page_with_frame.html');
    const original = await driver.getWindowHandle();
    const before = new Set(await driver.getAllWindowHandles());

    await driver.findElement(By.linkText('Open new window')).click();
    await driver.wait(async () => {
      const handles = await driver.getAllWindowHandles();
      return handles.some(handle => !before.has(handle));
    }, 10000);

    const handles = await driver.getAllWindowHandles();
    const added = handles.filter(handle => !before.has(handle));
    if (added.length !== 1) throw new Error(`Expected one new window, got ${added.length}`);

    await driver.switchTo().window(added[0]);
    await driver.wait(until.titleIs('Simple Page'), 10000);
    const png = await driver.takeScreenshot();
    require('fs').writeFileSync('target-tab.png', Buffer.from(png, 'base64'));

    await driver.switchTo().window(original);
  } finally {
    await driver.quit();
  }
})();

These examples assume the Selenium binding and a compatible browser driver are installed and available to the runtime. Selenium Manager can assist with driver management in current Selenium releases; consult the official Selenium documentation for setup details relevant to your language and environment.

Or skip the browser setup

If you need a screenshot of a public page rather than a browser automation test, ScreenshotNeo returns a screenshot from one API request. See the ScreenshotNeo API documentation for options.

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card.

Troubleshooting

Symptom Likely cause Fix
The screenshot shows the original page The click opened a tab, but the driver stayed on the original handle. Wait for the new handle, switch to it, then verify URL or title before capture.
The wait for two windows times out The action did not open a new top-level context, a popup was blocked, or the expected count is wrong because other tabs already exist. Check the action and initial handle count. Wait for a handle set difference if the starting count varies. If the page navigates in the same tab, do not wait for another handle; wait for the destination URL instead.
NoSuchWindowException / “no such window” The selected tab was closed, or the driver was left on a context that no longer exists. Check driver.window_handles before switching. After closing a tab, switch to a known surviving handle. Do not reuse a handle after its window closes.
The new handle exists but the screenshot is blank or incomplete The top-level context opened before its document finished loading or before the page’s content was ready. After switching, wait for a stable URL, title, or page-specific element. For dynamically rendered pages, wait for the relevant content rather than relying only on the handle count.
The test sometimes picks the wrong newly opened tab More than one context opened, and the code selected an arbitrary handle or list position. Compare against the complete pre-action handle set and match each new candidate by URL, title, or a page marker.
The browser visibly focuses another tab, but Selenium reports the old handle Operating-system focus and WebDriver’s selected context are separate state. Use switch_to.window(handle) explicitly. Do not use desktop focus as evidence of the driver’s current handle.
The element screenshot fails or captures the wrong content The element lookup happened in the wrong context, or the element is absent/not ready in the selected page. Switch first, wait for the element in that page, then call the element screenshot method. Use a page screenshot if you need the full current page.

Performance, reliability, and cost

Explicit waits improve reliability without adding a fixed delay to every run: they return as soon as the handle or page condition is satisfied, up to the timeout. Keep the wait bounded, and wait for the condition that matters. A handle appearing means the browsing context exists; it does not guarantee that client-rendered content is complete.

Capturing a screenshot adds browser work and file output to the test, so capture only the context and scope the test needs. Element screenshots can avoid saving a full page when the requirement is a single component. Selenium’s documentation does not provide a universal timing or cost benchmark for this fix; runtime depends on the page, browser, driver, and execution environment. Selenium itself does not charge per screenshot; account for the browser infrastructure and test runtime you operate.

FAQ

Does Selenium automatically switch to the tab that appears in front?

No. Screen focus does not tell WebDriver which context your test intends to control. Switch using the target window handle.

Can I use a tab index to choose the new tab?

A list index is fragile when tabs already exist or their ordering changes. Compare handle sets and identify the intended page using its URL or title.

Should I use a page screenshot or an element screenshot?

Use a page screenshot for the selected page. Use an element screenshot when the expected artifact is one particular element in that same selected context.

How do I take a screenshot after closing the popup tab?

Switch back to a surviving handle after closing the popup, then capture. A closed tab cannot remain the driver’s current context.