ScreenshotNeo

BlogGuides

Why Selenium 4 Is a Major Version: Breaking Changes and Migration

Selenium 4 adopts W3C WebDriver behavior and removes legacy protocol support. Learn what to update in capabilities, bindings, and driver setup.

By the ScreenshotNeo team4 October 20269 min read

Selenium 4 is a major version because it completes Selenium’s move away from legacy JSON Wire Protocol behavior and uses the W3C WebDriver standard. Most Selenium 3 tests that already used W3C-compatible sessions may need few changes. Tests that depend on legacy capabilities, protocol conversion, or removed binding APIs can fail to compile or create sessions.

For migration, update the binding, replace obsolete APIs, express session settings through browser-specific Options objects and W3C capabilities, review driver provisioning, then run representative tests against the browsers, Grid, and cloud providers you actually use. The exact changes depend on your language binding and Selenium version.

1. Why Selenium 4 is a major version

During the transition to W3C WebDriver, Selenium 3 supported both the W3C protocol and the older JSON Wire Protocol. That compatibility required handshake and conversion logic to translate legacy capabilities and commands. The Selenium project describes edge cases and maintenance burden from that translation. Selenium 4 removes legacy protocol support and uses W3C WebDriver behavior.

The effect is greatest when a test suite or provider integration relies on old capability names, free-form capability maps, or assumptions about protocol conversion. W3C-compliant Selenium 3 sessions should generally continue to work, though binding API changes and environment compatibility still need review. See the official Selenium 4 upgrade guide and the project’s background on removing legacy protocol support.

2. Migration checklist

  1. Inventory the test environment. Record the language binding and exact Selenium version, browser and driver versions, local or remote execution, Grid version, cloud provider, and how drivers are selected.
  2. Update the Selenium dependency. Select a Selenium 4 release compatible with your runtime and environment, following your normal dependency pinning policy.
  3. Audit capabilities. Move browser configuration into the browser’s Options class. Use standard W3C capability names for standard settings, and the provider’s documented vendor-prefixed options container for provider-specific settings.
  4. Replace removed or changed APIs. Search test code and shared helpers, not just individual tests. Binding-specific examples are below.
  5. Review driver provisioning. Decide whether Selenium Manager fits the environment or whether pinned, preinstalled browser and driver binaries remain necessary.
  6. Compile and execute representative tests. Cover session creation, waits, actions, custom capabilities, each supported browser, and each local, Grid, or cloud execution path.

3. Capabilities: move to W3C-compatible Options

Prefer a browser-specific Options object over legacy DesiredCapabilities patterns or an unstructured capability map. W3C standard capability names include:

Capability Purpose Migration note
browserName Browser type Usually supplied by the browser Options class.
browserVersion Requested browser version Use the standard spelling and verify provider support.
platformName Requested platform Use the standard spelling rather than a legacy alias.
acceptInsecureCerts Accept invalid certificates Set deliberately; it affects test behavior and security assumptions.
pageLoadStrategy When navigation is considered complete Check that the chosen strategy fits the test’s synchronization.
proxy Proxy configuration Use the binding’s supported proxy configuration object.
timeouts Session timeout settings Review binding APIs and units as well as the capability shape.
unhandledPromptBehavior Handling of unexpected prompts Confirm the desired behavior for alerts and dialogs.

Cloud-specific values such as build labels or test names are not standard WebDriver capabilities. Put them in the provider’s documented vendor-prefixed options block. An unprefixed, non-standard capability that happened to work through Selenium 3 conversion may now be rejected or ignored. Consult both the Selenium migration guide and your provider’s current capability documentation.

4. Binding-specific code changes

Java: use Duration for waits and timeouts

Timeout and wait APIs use java.time.Duration in place of the older (long, TimeUnit) arguments. For example:

import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;

public class SeleniumFourExample {
  public static void main(String[] args) {
    WebDriver driver = new ChromeDriver();
    try {
      driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0));
      driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));
      driver.get("https://example.com");
      new WebDriverWait(driver, Duration.ofSeconds(10))
          .until(d -> d.findElement(By.tagName("h1")).isDisplayed());
    } finally {
      driver.quit();
    }
  }
}

Update WebDriverWait, FluentWait.withTimeout, pollingEvery, and driver timeout calls to use Duration values. Selenium’s Java FindsBy utility interfaces were removed; use By locators through the WebDriver API.

Python: use Service, Options, and By

Use Service to provide an explicit driver executable, or omit it and let Selenium Manager resolve the driver when that suits your setup. Use find_element(By..., ...); the find_element_by_* methods were removed in Selenium 4.3. The executable_path and desired_capabilities constructor keyword arguments were removed in 4.10.

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

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")

# If chromedriver is already on PATH, Selenium Manager can resolve it:
driver = webdriver.Chrome(options=options)

# Alternatively, provide a pinned driver executable explicitly:
# service = Service(executable_path="/path/to/chromedriver")
# driver = webdriver.Chrome(service=service, options=options)

try:
    driver.set_page_load_timeout(30)
    driver.get("https://example.com")
    heading = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.TAG_NAME, "h1"))
    )
    print(heading.text)
finally:
    driver.quit()

For a remote session, pass an Options object to webdriver.Remote and use the current Remote API for your Selenium version. Do not pass removed desired_capabilities arguments.

C#: use AddAdditionalOption for provider options

Replace deprecated AddAdditionalCapability calls with AddAdditionalOption for additional options. Keep standard browser settings on the browser Options object and follow your provider’s documented prefix and nesting for vendor options.

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

var options = new ChromeOptions();
options.AddArgument("--headless=new");
// Add provider-specific values using the provider's documented options key.
// options.AddAdditionalOption("vendor:options", vendorOptions);

using IWebDriver driver = new ChromeDriver(options);
driver.Navigate().GoToUrl("https://example.com");
Console.WriteLine(driver.FindElement(By.TagName("h1")).Text);

These are documented examples, not a complete changelog for every binding. Check the upgrade page and the release notes for your binding before declaring a migration finished.

5. Driver setup: Selenium Manager or managed binaries

Selenium Manager is included with Selenium beginning in version 4.6. It can discover an installed browser, resolve a matching driver, download it, and cache it. The Selenium project documentation says browser download support begins with Selenium 4.11. This can simplify ordinary local setups, but it does not remove environment constraints.

Approach Useful when Check before adopting
Selenium Manager Standard developer machines or environments where automatic resolution is acceptable. Network access, proxies, browser availability, cache behavior, and whether automatic downloads comply with policy.
Preinstalled and pinned binaries Reproducible CI images, restricted networks, or a policy requiring explicit browser and driver versions. Keep browser and driver versions aligned and update the image deliberately.

For isolated networks, custom browser images, or strict pinning, validate provisioning in the actual runtime. Selenium Manager’s documentation and milestones are covered in the project’s Selenium documentation and Python API documentation.

6. Validate local, Grid, and cloud sessions

Do not use a successful local Chrome session as proof that every target environment is migrated. At minimum, exercise one session creation path for each relevant browser and execution destination. Include test cases that use waits, Actions, custom capabilities, proxy or certificate settings, and provider-specific metadata if your suite relies on them.

  1. Compile or import the project to catch removed method and constructor signatures.
  2. Start a basic browser session and verify navigation and clean shutdown.
  3. Run a test using explicit waits and one using the Actions API.
  4. Run through each Grid or cloud configuration and inspect returned session capabilities and provider logs.
  5. Run against the browser versions used in CI and deployment, then pin or adjust versions based on the results.

This validation plan follows from the documented protocol and API changes; compatibility varies by binding, browser, Grid, and provider. The official materials do not establish one compatibility matrix covering every combination.

7. Common migration errors and fixes

Symptom Likely cause Fix
Session creation fails with an invalid or unknown capability A legacy capability name or an unprefixed provider-specific value is being sent. Use the W3C name for standard settings and the provider’s documented vendor-prefixed options container for custom settings.
Compilation fails on a wait or timeout call The code uses the older numeric value plus TimeUnit signature. Pass a Duration in Java and update all related wait helpers.
Python raises an unexpected keyword argument error Code still passes executable_path or desired_capabilities. Use Service and browser Options with the current constructor pattern.
Python cannot find an old locator method The suite calls a removed find_element_by_* method. Import By and call find_element(By.ID, "...") or another locator strategy.
Provider settings disappear or are rejected Non-standard values were attached as generic capabilities rather than under the provider’s options key. Follow that provider’s current W3C capability format and verify the resulting session configuration.
Driver startup fails only in CI The environment cannot reach download endpoints, lacks a browser, uses a proxy, or enforces pinned binaries. Check network and proxy configuration; use an explicitly provisioned browser and driver where required.
Tests time out after the upgrade Synchronization depended on timing or page-load assumptions, or a wait API was converted incorrectly. Use explicit waits for the required condition, review page-load strategy and timeout units, and avoid masking failures with excessively long waits.

8. Performance, reliability, and cost considerations

The protocol migration itself does not establish a universal performance gain or slowdown. Measure your suite in its real execution environment if runtime matters. Selenium Manager can reduce manual driver setup, while downloading browser or driver binaries at runtime introduces dependence on network access and cache state. Preinstalled, pinned binaries make provisioning more controlled but require image maintenance.

For reliability, pin Selenium and browser versions as appropriate for your release process, record the Grid or provider configuration, and keep session creation checks in CI. Avoid treating a passing local run as evidence that remote capabilities or browser versions will behave identically.

There is no single migration cost figure: effort depends on how much legacy API usage and custom session configuration the project has. The practical cost is usually in dependency and capability audits, updating binding-specific calls, and validating each supported execution path. The supplied official sources do not provide benchmark or migration-time statistics.

9. Capture screenshots without managing a browser

For Selenium automation, the migration above remains the way to update your WebDriver tests. If your task is simply to capture a website image or PDF, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It takes a URL in one GET request and returns PNG, JPEG, WebP, or PDF. The API also supports custom CSS and JavaScript, viewport options, full-page captures, element selection, and other capture settings; see the ScreenshotNeo API documentation.

Or skip the browser setup

Use the direct API call when you need a screenshot rather than an interactive Selenium session. This cURL example saves a WebP capture:

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

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)

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card required.

10. Frequently asked questions

Will every Selenium 3 test break on Selenium 4?

No. Tests already using W3C-compliant sessions may need little protocol-related change. Removed APIs, old capability formats, and environment differences can still require updates.

Do I need Selenium Manager?

No. It is one driver provisioning option. Use it when its discovery and download behavior fits your environment; retain managed binaries if network access, reproducibility, or policy requires them.

Is Selenium 4 a browser upgrade?

No. Selenium is the browser automation binding and tooling. Browser and driver versions are separate environment components that you should validate alongside the Selenium dependency.

Where should cloud-only capabilities go?

Use the cloud provider’s documented vendor-prefixed options structure, and verify the resulting session rather than assuming an arbitrary capability will be accepted.

Sources