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.

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

Short answer: stop trying to repair a new PhantomJS setup. PhantomJS development is suspended, and Selenium deprecated its integration in favor of headless Chrome or Firefox. Create an isolated Python environment, upgrade Selenium, let Selenium Manager find the browser driver, then diagnose discovery, session-startup, and page-synchronization errors as separate problems.

Why PhantomJS errors keep appearing

PhantomJS is not a current Selenium target. Selenium’s 3.8.1 change log states: “PhantomJS is now deprecated, please use either Chrome or Firefox in headless mode.” The PhantomJS project page says, “Important: PhantomJS development is suspended until further notice.” Its maintainers identified the lack of active contribution as the reason for suspension; version 2.1.1 remained the last known stable release.

That means errors such as WebDriverException, missing executables, unsupported capabilities, and session failures are usually symptoms of an obsolete toolchain rather than a missing PhantomJS option. Replace webdriver.PhantomJS(...) and PhantomJS-specific desired capabilities with a supported browser.

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

Start with a clean, current Python setup

1. Record the environment

Before changing code, write down:

  • Python version and operating system
  • Selenium package version (python -m pip show selenium)
  • Installed Chrome or Firefox version
  • Whether the test runs locally, in CI, or against a remote Selenium server
  • The complete exception and driver log

Version mismatches are common, but the compatible combination depends on the browser and release. Capturing these details prevents a “fix” that only works on one machine.

2. Use a virtual environment and upgrade Selenium

python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell: .venvScriptsActivate.ps1
python -m pip install --upgrade pip
python -m pip install --upgrade selenium
python -m pip show selenium

Current Selenium Python releases can invoke Selenium Manager when a WebDriver is created. Selenium Manager resolves or downloads the required browser driver in supported installations, so many old instructions telling you to download a driver manually are now legacy guidance. You still need the target browser installed unless your deployment image provides it another way.

Replace PhantomJS with headless Chrome or Firefox

Headless Chrome

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

options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    print(driver.title)
finally:
    driver.quit()

Headless Firefox

from selenium import webdriver
from selenium.webdriver.firefox.options import Options

options = Options()
options.add_argument("-headless")

driver = webdriver.Firefox(options=options)
try:
    driver.get("https://example.com")
    print(driver.title)
finally:
    driver.quit()

Both examples use Selenium’s current Options APIs and avoid a hard-coded driver path. Choose the browser whose JavaScript behavior, rendering, CI image, operating-system support, startup characteristics, and debugging tools best match the site you automate. Selenium’s deprecation notice establishes Chrome and Firefox as the migration targets; it does not establish a universal speed or reliability winner.

When an explicit driver path is unavoidable

Some locked-down CI images, air-gapped hosts, or enterprise policies require a preinstalled driver. Use Selenium’s Service object rather than the removed PhantomJS constructor:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

options = Options()
options.add_argument("--headless=new")
service = Service("/opt/webdriver/chromedriver")
driver = webdriver.Chrome(service=service, options=options)

Check that the file exists, is executable, and belongs to the browser version installed in the same image. If Selenium Manager is available, remove stale paths first; a path copied from an old tutorial can force Selenium to use an incompatible binary.

Diagnose the exception by class

NoSuchDriverException: Selenium cannot locate the driver

This is a discovery or installation failure, not a page-locator problem. Confirm that Chrome or Firefox is installed, upgrade Selenium, and inspect Selenium Manager’s diagnostic output. Then check:

  • Whether the driver is on PATH or supplied through a correct Service path
  • Executable permissions on Linux and macOS
  • That the CI container actually contains the browser and driver
  • Corporate proxy, download, or certificate restrictions that prevent Selenium Manager from obtaining a driver

Run the same script outside CI. If it works locally, compare the image, environment variables, permissions, and network policy rather than changing locators.

SessionNotCreatedException: the browser session cannot start

A session-creation failure occurs after Selenium has attempted to launch the browser. Compare browser and driver versions, remove obsolete hard-coded binaries, and read the driver log. In containers, verify the headless flags and sandbox policy required by that image; a flag that fixes one container can be inappropriate or insecure in another. Also check that another process is not occupying the debugging or profile resources your browser needs.

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

NoSuchElementException and timeout errors

A successful get() call only means navigation was requested. Dynamic content may still be loading, an element may be inside an iframe, or a consent overlay may cover the page. Selenium identifies poor synchronization as its most common reported error.

Use an explicit wait for the state you actually need:

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

wait = WebDriverWait(driver, 20)
button = wait.until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-testid='buy']))
)
button.click()

Prefer stable IDs, accessible labels, or purpose-built data attributes over brittle absolute XPath. If a wait expires, inspect the current URL and page source, verify the locator in browser developer tools, and confirm that the expected element is not in a different frame or window.

Frames and windows

Switch into the iframe before locating its contents:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
frame = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "iframe.payment")))
driver.switch_to.frame(frame)
wait.until(EC.visibility_of_element_located((By.NAME, "cardnumber")))
driver.switch_to.default_content()

For a new tab, wait for the window count, then switch explicitly:

old_handles = driver.window_handles
# trigger the link or action here
wait.until(lambda d: len(d.window_handles) > len(old_handles))
new_handle = next(h for h in driver.window_handles if h not in old_handles)
driver.switch_to.window(new_handle)

Stale, intercepted, and non-interactable elements

StaleElementReferenceException means the page replaced the node after you located it. Locate it again after the update. ElementClickInterceptedException commonly indicates an overlay, animation, or another element covering the target; wait for the overlay to disappear and for the target to be clickable. ElementNotInteractableException means the node exists but is hidden, disabled, or otherwise not ready. Scrolling, waiting for visibility, or using the correct control can fix it; JavaScript clicking should be a last resort because it can bypass the user behavior your test is meant to verify.

A repeatable troubleshooting workflow

  1. Reduce the case. Reproduce with one URL and one action, keeping the full stack trace.
  2. Verify browser startup. Run a script that only opens a page and prints its title.
  3. Classify the failure. Separate driver discovery, session creation, navigation, synchronization, frame/window, and interaction errors.
  4. Turn on useful logs. Preserve Selenium Manager and browser-driver logs in CI artifacts; record Python, Selenium, browser, driver, and OS versions.
  5. Check the page state. Save the current URL and a screenshot when a wait fails. This distinguishes a redirect, login page, bot check, blank response, or changed markup.
  6. Try another browser. Repeating the operation in Chrome and Firefox helps determine whether the defect is in your Selenium code or an underlying browser driver.
  7. Make synchronization explicit. Replace fixed sleeps with waits for presence, visibility, clickability, a URL change, or a custom condition.
  8. Rebuild the CI image. Ensure the browser, fonts, shared libraries, permissions, and sandbox configuration are present and consistent with local development.

Reliability and performance choices

Headless mode removes the visible window but does not remove browser rendering, JavaScript, network, or security behavior. Keep browser profiles isolated between parallel jobs, call quit() in a finally block, and avoid sharing a driver instance across unrelated tests. Use a realistic page-load and explicit-wait policy instead of one very long global timeout: long waits hide regressions, while short waits create false failures on a busy CI runner.

Do not assume Chrome or Firefox is universally faster. Measure startup time, memory use, navigation behavior, and test stability in your own operating system and deployment image. A cross-browser pass is also a useful compatibility check for the application under test.

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

If your goal is a clean image or PDF rather than interactive browser testing, ScreenshotNeo provides a website screenshot API and MCP server. It accepts a URL in one request and can return PNG, JPEG, WebP, or PDF. Before capture, it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

See the full parameter list and OpenAPI details in the ScreenshotNeo documentation. A minimal cURL request is:

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}`);

ScreenshotNeo also offers an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools. Its options include full-page lazy-image capture, CSS-element capture, dark mode, device presets, custom viewport and retina scale, PDF paper settings, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, and a usage API. Every feature is on every plan: 1,000 shots per month free with no card; paid plans start at $5 for 3,000 shots.

Create a free ScreenshotNeo account to use the 1,000 monthly shots without entering a card.

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

Frequently Asked Questions

Can I keep PhantomJS installed for an old test suite?

You can preserve an isolated legacy environment for historical runs, but it will not receive ongoing development. New tests should target headless Chrome or Firefox.

Should I use implicit and explicit waits together?

Use one deliberate synchronization strategy. Mixing a long implicit wait with explicit waits can make timeout behavior difficult to predict; explicit waits with clear conditions are easier to diagnose.

Does headless mode test exactly what a headed browser displays?

It exercises the same browser engine, but viewport size, GPU behavior, fonts, permissions, and environment settings can differ. Set the viewport deliberately and validate important flows in the deployment environment.

When should I use a remote Selenium server instead of a local driver?

Use a remote server when browsers are centrally managed, distributed across machines, or required for a browser matrix. The same discovery and synchronization distinctions still apply; remote-server logs and network reachability become additional checks.

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

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.