How to Handle Alerts and Windows in Selenium with C#
Handle JavaScript alerts and prompts, switch between tabs and windows, and avoid common Selenium C# context errors with runnable examples.
Use driver.SwitchTo().Alert() for JavaScript alerts, confirmations, and prompts. Use window handles with driver.SwitchTo().Window(handle) to move between tabs or windows. Save the original handle before triggering a new context, wait for the new handle to appear, and switch back to a handle that still exists after closing a child window.
1. Set up Selenium WebDriver for C#
Install the Selenium WebDriver and Selenium Support packages for your project, and configure a driver for the browser you run in CI or locally. The examples below assume driver is an initialized IWebDriver. Keep the Selenium package versions aligned, and check your installed .NET API version if a method differs.
using System;
using System.Linq;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
// Create and configure your IWebDriver using the browser and driver setup
// appropriate for your project before running the examples.
2. Accept, dismiss, and read JavaScript alerts
An alert blocks normal interaction with the page until it is handled. Once the site action has caused the dialog to appear, get its IAlert from the active driver context. Read Text to assert its message, then call Accept() or Dismiss() according to the expected behavior.
IAlert alert = driver.SwitchTo().Alert();
string message = alert.Text;
Console.WriteLine(message);
alert.Accept();
// To cancel a confirmation dialog instead:
IAlert confirmation = driver.SwitchTo().Alert();
confirmation.Dismiss();
For a prompt, send the value before accepting:
IAlert prompt = driver.SwitchTo().Alert();
prompt.SendKeys("test input");
prompt.Accept();
IAlert exposes the dialog text, acceptance, dismissal, and prompt input methods in Selenium's [.NET API reference](https://www.selenium.dev/selenium/docs/api/dotnet/webdriver/OpenQA.Selenium.IAlert.html). Selenium's guide covers [JavaScript alerts, prompts, and confirmations](https://www.selenium.dev/documentation/webdriver/browser/alerts/).
Wait for an alert that appears asynchronously
If the page triggers the dialog after an asynchronous operation, wait for it rather than immediately calling SwitchTo().Alert(). Selenium's .NET WebDriverWait can poll a condition:
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IAlert alert = wait.Until(d =>
{
try
{
return d.SwitchTo().Alert();
}
catch (NoAlertPresentException)
{
return null;
}
});
string text = alert.Text;
alert.Accept();
The lambda returns null until an alert exists, allowing the wait to retry. Choose a timeout that fits the operation and your test environment. The [.NET WebDriverWait reference](https://www.selenium.dev/selenium/docs/api/dotnet/webdriver/OpenQA.Selenium.Support.UI.WebDriverWait.html) documents condition-based waits.
3. Switch to a tab or window opened by the site
WebDriver identifies browser tabs and windows with handles. Capture the current handle before the click that opens another context, wait for the handle count to change, find the handle that was not present before, and switch to it.
string original = driver.CurrentWindowHandle;
var handlesBefore = driver.WindowHandles.ToHashSet();
driver.FindElement(By.LinkText("Open new window")).Click();
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.WindowHandles.Count > handlesBefore.Count);
string child = driver.WindowHandles.Single(handle => !handlesBefore.Contains(handle));
driver.SwitchTo().Window(child);
// Interact with the child page here.
Console.WriteLine(driver.Title);
driver.Close();
driver.SwitchTo().Window(original);
This set-difference pattern also works when a browser already has more than one context. If the application can open multiple contexts from a single action, collect all handles absent from the original set and identify the intended one by its URL, title, or expected page content. Selenium's official [windows and tabs guide](https://www.selenium.dev/documentation/webdriver/interactions/windows/) shows the basic wait, handle discovery, switch, close, and return flow.
When there are several windows
Do not assume a child is always at index 1. Handle ordering is not a reliable way to identify a particular page. Compare handle sets, then inspect candidate pages where needed:
string original = driver.CurrentWindowHandle;
var before = driver.WindowHandles.ToHashSet();
// Trigger an action that may open one or more contexts.
driver.FindElement(By.Id("open-report")).Click();
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.WindowHandles.Count > before.Count);
foreach (string handle in driver.WindowHandles.Where(h => !before.Contains(h)))
{
driver.SwitchTo().Window(handle);
if (driver.Url.Contains("report", StringComparison.OrdinalIgnoreCase))
{
break;
}
}
// Continue with the identified context; return to original when done.
driver.SwitchTo().Window(original);
For multiple new windows, adapt the wait to the expected number or wait for a page-specific condition. Always ensure the handle selected by the loop is the intended one before continuing.
4. Create a new tab or window directly (Selenium 4+)
If the test itself needs a fresh context, Selenium 4 and later can create it and focus it in one operation:
driver.SwitchTo().NewWindow(WindowType.Tab);
// The new tab is already the active WebDriver context.
driver.Navigate().GoToUrl("https://example.com");
For a separate browser window, use WindowType.Window. The new context is focused by the call, so another SwitchTo().Window(...) is unnecessary. This differs from handling a context opened by the application: there, first discover the new handle. See Selenium's [window and tab documentation](https://www.selenium.dev/documentation/webdriver/interactions/windows/).
5. Know which Selenium API to use
| Situation | API | Typical action |
|---|---|---|
| JavaScript alert | IAlert |
Read text, accept, or dismiss |
| JavaScript confirmation | IAlert |
Accept to confirm or dismiss to cancel |
| JavaScript prompt | IAlert |
Send text, then accept or dismiss |
| Site opens a tab/window | Window handles | Wait for handle, discover it, switch |
| Test needs a fresh tab/window | NewWindow (Selenium 4+) |
Create and focus the context |
The alert target locator is part of Selenium's [.NET ITargetLocator API](https://www.selenium.dev/selenium/docs/api/dotnet/webdriver/OpenQA.Selenium.ITargetLocator.html); the active browser context is managed through the [IWebDriver API](https://www.selenium.dev/selenium/docs/api/dotnet/webdriver/OpenQA.Selenium.IWebDriver.html).
6. Troubleshoot common errors
| Error or symptom | Likely cause | Fix |
|---|---|---|
NoAlertPresentException |
The alert has not appeared yet, or the current context has no alert. | Wait for the alert, verify the triggering action completed, and check that the correct window is active. |
UnhandledAlertException |
A dialog is open while a command requiring the page is issued. | Switch to the alert and accept or dismiss it before issuing page commands. |
NoSuchWindowException |
The driver is still on a window that was closed, or the handle is stale. | Switch to a handle from the current WindowHandles collection. Save the original handle and return to it after closing a child. |
| Timeout waiting for a second window | The click did not open a context, or the site opened it more slowly than expected. | Verify the locator and click result; wait on a count greater than the starting count rather than hard-coding two if multiple contexts may exist. |
| Wrong tab receives commands | The driver was not switched to the intended handle. | Track handles before the action and identify new handles by set difference; validate URL, title, or page content. |
NewWindow is unavailable |
The project uses an older Selenium version. | Upgrade to Selenium 4 or later, or use the site's normal open action and discover its handle. |
| Prompt value is missing | SendKeys was called on a page element or after the prompt was accepted. |
Obtain the prompt using SwitchTo().Alert(), send text, then accept. |
Selenium specifically warns that closing a window without switching back leaves WebDriver attached to the closed page and causes a No Such Window failure. Make returning to a still-open handle part of cleanup, including in test teardown when exceptions can interrupt the happy path.
7. Reliability, runtime, and test cost
- Wait for state changes: Use a bounded wait for alerts or new handles instead of fixed sleeps. Fixed delays make fast runs slower and still fail when the environment is slower than expected.
- Clean up contexts: Close only the child context when you are finished, then switch back to a surviving handle. If a test may fail midway, put cleanup in a
finallyblock and check that the target handle still exists before switching. - Identify contexts by evidence: Compare handle sets and verify the candidate's URL or content. Avoid assumptions based on handle list position.
- Keep assertions close to the dialog: Capture alert text before accepting it, then assert the result in the page after the dialog closes.
- Budget for browser work: Each extra browser interaction and wait contributes to test runtime. Use the shortest timeout consistent with the application and CI environment, and avoid opening contexts the test does not need.
8. Or skip the browser setup
If your goal is to capture a page rather than test its interactive behavior, ScreenshotNeo provides a screenshot API and MCP server. Its API accepts a URL in one GET request. See the ScreenshotNeo API documentation for parameters and 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}`);
- Cookie banners are accepted where possible, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before capture; each step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Response headers say the page verdict and whether the shot was billed.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.
Sign up free for 1,000 screenshots a month, no card required.
9. FAQ
Can I read an alert without accepting it first?
Yes. Read alert.Text before deciding whether to call Accept() or Dismiss().
Does switching to a window close the original tab?
No. Switching changes the active WebDriver context. Close a context explicitly with driver.Close() when the test is done with it.
Should I use Selenium alerts for a browser notification?
The alert API here is for JavaScript alert, confirmation, and prompt dialogs. A page's own HTML modal is a regular page element and should be located and interacted with as such.
Can a handle be reused after its window closes?
No. Treat a closed context's handle as invalid and switch to one of the handles that remains open.


