PRACTICAL GUIDE / WebDriver capability negotiation
WebDriver Capability Negotiation with alwaysMatch and firstMatch
Understand WebDriver capability negotiation with alwaysMatch, firstMatch, Selenium RemoteWebDriver, conflict rules, and session evidence.
In this guide12 sections
- Read the New-Session Payload as a Contract
- Put Invariants in alwaysMatch
- Put Alternatives in firstMatch
- Avoid Duplicate-Key Merge Failures
- Use Selenium's Java Builder for Alternatives
- Distinguish Matching from Scheduling
- Inspect the Session Response
- Diagnose Failures in Protocol Order
- Account for Binding Differences
- Weigh Flexibility Against Reproducibility
- Operational Checklist
- Conclusion: Negotiate Deliberately
What you will learn
- Read the New-Session Payload as a Contract
- Put Invariants in alwaysMatch
- Put Alternatives in firstMatch
- Avoid Duplicate-Key Merge Failures
Capability negotiation is the contract negotiation that happens before WebDriver can execute a single browser command. A local binding or remote Grid receives a new-session request, validates its shape, combines common requirements with each alternative, finds a compatible endpoint, and returns the capabilities of the session it actually created.
Most Selenium users should build requests with typed browser options, not hand-written JSON. Senior automation engineers still need to understand the wire model because malformed merges, impossible platform constraints, and misplaced vendor options all surface as session-creation failures far from the test scenario.
Read the New-Session Payload as a Contract
The W3C WebDriver standard defines the POST /session request and the capability-processing rules. The outer capabilities object can contain alwaysMatch, an object of shared requirements, and firstMatch, an array of alternative objects. Conceptually, the remote end produces candidates by merging alwaysMatch with each firstMatch entry and then attempts to match one candidate.
The Selenium Remote WebDriver guide keeps ordinary code at a safer level: provide a remote address and one or more browser option objects. The binding serializes those typed values into the protocol shape. Understanding the result lets you diagnose the boundary without replacing the binding with a custom protocol client.
Animated field map
WebDriver New-Session Negotiation
Typed options become a new-session payload whose shared requirements are merged with alternatives before one browser slot is selected.
01 / options objects
Options Objects
Declare browser alternatives and standard or vendor-specific settings.
02 / session payload
New Session Payload
Serialize one capabilities object with shared and alternative layers.
03 / always merge
alwaysMatch Merge
Combine every shared requirement with one alternative without duplicate keys.
04 / first candidate
firstMatch Candidate
Validate and evaluate each ordered candidate against endpoint support.
05 / browser slot
Matched Browser Slot
Create one session and return its authoritative capabilities.
Put Invariants in alwaysMatch
An invariant must be true whichever browser candidate is selected. Platform, certificate policy, page-load strategy, prompt handling, proxy configuration, and a namespaced test label can belong here when they genuinely apply to every alternative. Do not promote a value merely to reduce repetition; placement changes the negotiation semantics.
For example, platformName: linux rules out every non-Linux endpoint before browser-specific details matter. That is appropriate when the application relies on Linux-only infrastructure or when the job promises a Linux environment. It is harmful when the real requirement is simply to obtain Chrome or Firefox from any approved platform.
Only standard capabilities have unprefixed names. Extensions must contain a vendor prefix separated by a colon. Provider credentials should not be embedded in a payload that will be printed for diagnostics. Prefer endpoint authentication or the provider's secret-handling guidance.
Put Alternatives in firstMatch
Each firstMatch object describes one acceptable way to satisfy the request. Browser identity and its vendor options naturally travel together. The following illustrative payload asks for one Linux session: Chrome with its arguments or Firefox with its arguments. It does not ask WebDriver to run both.
{
"capabilities": {
"alwaysMatch": {
"platformName": "linux",
"acceptInsecureCerts": false,
"pageLoadStrategy": "normal",
"se:name": "checkout smoke"
},
"firstMatch": [
{
"browserName": "chrome",
"goog:chromeOptions": {
"args": ["--headless=new", "--window-size=1440,900"]
}
},
{
"browserName": "firefox",
"moz:firefoxOptions": {
"args": ["-headless"]
}
}
]
}
}Alternative order communicates preference, but it is not a general-purpose retry, load-balancing, or test-matrix mechanism. The intermediary and endpoint still apply their scheduling and matching rules. If product sign-off requires both browsers, issue one deterministic request for Chrome and another for Firefox so each result is visible.
Avoid Duplicate-Key Merge Failures
The merge rule is intentionally strict. If alwaysMatch includes platformName and a firstMatch candidate also includes platformName, the request is invalid rather than an override operation. Identical values do not make the duplication legal. This catches ambiguous clients and keeps every final candidate mechanically explainable.
Do not place a broad value in alwaysMatch and expect a candidate to specialize it. Instead, move the differing property entirely into each candidate. The same rule applies to vendor option objects: a goog:chromeOptions object cannot be split across both layers and deep-merged by assumption. Construct one complete vendor object in the Chrome candidate.
Capture the serialized request at debug level with secrets redacted. A compact structural diff is often enough to reveal a duplicate key, wrong value type, unprefixed extension, or an empty firstMatch array before Grid logs are needed.
Use Selenium's Java Builder for Alternatives
Java's RemoteWebDriver.builder() exposes the intent directly. oneOf supplies browser alternatives, while setCapability applies shared values. This is clearer than constructing nested maps and lets Selenium serialize browser namespaces from their option classes.
import java.net.URI;
import java.util.Map;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
public final class NegotiatedSession {
public static WebDriver start(URI gridUri, String testName) throws Exception {
ChromeOptions chrome = new ChromeOptions();
chrome.addArguments("--headless=new", "--window-size=1440,900");
FirefoxOptions firefox = new FirefoxOptions();
firefox.addArguments("-headless");
return RemoteWebDriver.builder()
.address(gridUri.toURL())
.oneOf(chrome, firefox)
.setCapability("platformName", "linux")
.setCapability("acceptInsecureCerts", false)
.setCapability("pageLoadStrategy", "normal")
.setCapability("se:name", testName)
.build();
}
public static Map<String, Object> evidence(WebDriver driver) {
Capabilities actual = ((RemoteWebDriver) driver).getCapabilities();
return Map.of(
"browserName", actual.getBrowserName(),
"browserVersion", actual.getBrowserVersion(),
"platformName", actual.getPlatformName().toString());
}
}Wrap the returned driver in try/finally at the fixture boundary and always call quit. The builder automatically augments supported remote features, but augmentation does not change which capabilities matched. The response remains the source of truth.
Distinguish Matching from Scheduling
Capability matching asks whether an endpoint can satisfy a candidate. Scheduling decides when and where an intermediary such as Grid assigns that request. A valid request can wait because all compatible slots are busy. An impossible request can remain queued or fail depending on infrastructure policy. Those are different conditions and should produce different alerts.
Avoid assuming version comparison works identically across every remote implementation. The standard leaves aspects of browser-version matching implementation-defined because browser version schemes vary. Use values documented by the Grid or provider, and verify the returned version at the precision your test policy requires.
Platform vocabulary also needs discipline. Normalize it at the matrix boundary and use the values supported by the target Grid. A typo in platformName is not an application-test failure, and increasing element timeouts will never fix it.
Inspect the Session Response
A successful response contains a session ID and the matched capabilities. Archive a sanitized subset with the test report: browser name, browser version, platform name, page-load strategy, certificate policy, and the provider's session identifier. This proves which alternative won and supports direct correlation with Grid logs or video.
Assert invariants after creation. If the request promises Linux and a particular browser family, fail setup immediately when the response disagrees. Keep version assertions aligned with the request: a major-version constraint should not accidentally require an exact patch string, while a hermetic image with a fully pinned browser may require exact matching.
Do not infer the chosen candidate from list order. A returned Chrome session proves Chrome matched; being first in the request does not. Evidence belongs to the response, not to assumptions about routing.
Diagnose Failures in Protocol Order
An invalid argument usually means request validation failed: wrong data type, invalid standard value, unknown unprefixed capability, or duplicate properties during merging. A session not created response means processing got farther but the endpoint could not create the requested session. A queue timeout from an intermediary may mean the candidate is valid but no matching capacity became available.
Investigate in the same order as the protocol. First validate JSON shape and extension prefixes. Then expand each merged candidate on paper or with a small unit test. Next compare candidate constraints with Grid stereotypes or node configuration. Finally inspect driver and browser startup logs. This order prevents application-level retries from masking a setup contract that can never succeed.
Account for Binding Differences
Java's builder offers an explicit oneOf API. Other Selenium bindings commonly create remote sessions from one options object, and their high-level APIs for alternatives can differ. Use the current binding documentation rather than manually reproducing Java syntax. When a binding does not expose a convenient alternatives builder, separate deterministic matrix requests are often clearer than dropping to raw HTTP.
Across bindings, keep the semantic rules unchanged: standard unprefixed capabilities, namespaced extensions, no duplicate keys across merge layers, fresh options per session, and verification of returned capabilities. Raw payload logging should be opt-in and sanitized consistently in every language.
Weigh Flexibility Against Reproducibility
firstMatch is useful when either of several session types is genuinely acceptable, such as an infrastructure health check that needs any supported browser. It reduces caller-side branching and lets the remote end select available compatible capacity. The tradeoff is reduced predictability: reports, baselines, and browser-specific failures depend on the selected candidate.
For release gates and compatibility claims, deterministic one-browser requests are usually stronger. They produce stable naming, independent pass/fail outcomes, and straightforward capacity planning. Use negotiation flexibility only where substitutability is part of the requirement.
Operational Checklist
- Build capabilities with typed browser options whenever possible.
- Put only true cross-candidate invariants in
alwaysMatch. - Keep browser name and vendor options in the same alternative.
- Never duplicate a property across shared and candidate objects.
- Prefix every non-standard extension with its documented namespace.
- Treat
firstMatchas one-session alternatives, not matrix expansion. - Record a sanitized request and the returned capability subset.
- Separate malformed requests, impossible matches, queues, and startup errors.
- Verify browser, version precision, and platform immediately after creation.
- Quit every successful session even when setup assertions fail.
Conclusion: Negotiate Deliberately
Capability negotiation becomes predictable when every property has one layer and one reason to exist. Express invariants once, package each browser alternative completely, let Selenium serialize the protocol, and verify the returned session before tests proceed. When strict compatibility is the goal, create explicit sessions; when substitution is truly acceptable, use firstMatch and preserve evidence of what the remote end selected.
// LIVE COURSE / THE TESTING ACADEMY
Playwright Automation Mastery
Go beyond Selenium. Master Playwright with JS/TS in 90 days.
From the instructor behind this guide.
Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.
PRIMARY REFERENCES
Verify the details at the source
QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.
- 01Selenium documentation
Selenium Project
Canonical WebDriver, Grid, waits, element, and browser automation guidance.
- 02WebDriver standard
W3C
The browser automation protocol specification behind WebDriver implementations.
FAQ / QUICK ANSWERS
Questions testers ask
What is the difference between alwaysMatch and firstMatch?
alwaysMatch contains requirements shared by every candidate. firstMatch is an ordered array of alternative capability objects; the remote end merges the shared object with each candidate during session matching.
Can the same capability appear in alwaysMatch and firstMatch?
No. A duplicate property makes that merge invalid, even when both values look identical. Put each capability in exactly one layer of the request.
Does firstMatch run a test once in every listed browser?
No. It asks for one session that satisfies one candidate. A test matrix must create separate new-session requests when every browser is required.
Should vendor capabilities be placed in alwaysMatch?
Only when every candidate uses that vendor extension. Browser-specific namespaces such as goog:chromeOptions or moz:firefoxOptions normally belong in their matching firstMatch candidate.
How can a team prove which candidate WebDriver selected?
Inspect and archive the capabilities returned by the successful session, including browser name, browser version, platform, and relevant namespaced metadata.
RELATED GUIDES
Continue the learning route
GUIDE 01
Designing a Cross-Browser Options Matrix in Selenium WebDriver 4
Design a Selenium browser options matrix that separates W3C capabilities, vendor settings, environments, and verified cross-browser expectations.
GUIDE 02
Cross Browser Testing Guide: A Practical QA Workflow
Cross browser testing guide for QA teams covering browser matrix design, responsive checks, automation, defects, and release decisions with examples.
GUIDE 03
Selenium vs Playwright vs Cypress in 2026
Compare Selenium vs Playwright vs Cypress in 2026 for speed, browsers, CI, flakiness, ecosystem, and which web automation framework beginners should choose.
GUIDE 04
Selenium Manager Cache Strategy for Air-Gapped and Hermetic CI
Build a reproducible Selenium Manager cache for offline CI with pinned inputs, verified artifacts, cache isolation, and practical failure diagnostics.