Why does a W3C WebDriver endpoint node reject an unknown bare capability key but accept a colon-prefixed one?
answer
- a closed table and an escape hatch
- the colon is the passport
- first match wins, and some keys have no arm
- unknown namespaced keys keep their value
- only an endpoint node reaches invalid argument
basics
~20 sThe W3C WebDriver validation algorithm treats any key containing a colon as an extension capability and passes it through even when unrecognised, while an endpoint node must answer invalid argument for a non-standard key carrying no colon at all.
solid answer
~50 sThe specification's *validate capabilities* algorithm walks every key through an ordered switch. Standard keys such as `browserName` and `pageLoadStrategy` are type-checked. A key that is the key of an **extension capability** — and the spec requires such a key to contain a `:` — is deserialized in an implementation-specific way when the implementation knows it, and **otherwise its value is simply kept unchanged**. Only when no earlier arm matches and the remote end is an *endpoint node* does the algorithm return `invalid argument`. Selenium's client applies a **narrower** rule before the request even leaves: `AcceptedW3CCapabilityKeys` opens with the pattern `^[\w-]+:.*$`, which demands a word-or-hyphen prefix, so it passes the namespaced keys you meet in practice yet refuses some the spec permits — and refuses a bare non-standard key outright, `screenResolution` in the documentation of Selenoid (unmaintained, per its own README) being one. `NewSessionPayload` then rejects the whole payload.
code
java · 24 lines// org.openqa.selenium.AcceptedW3CCapabilityKeys (Selenium)
private static final Predicate<String> ACCEPTED_W3C_PATTERNS =
Stream.of(
"^[\\w-]+:.*$", // word-or-hyphen prefix, then a colon: narrower than the spec
"^acceptInsecureCerts$",
"^browserName$",
"^browserVersion$",
"^platformName$",
"^pageLoadStrategy$",
"^proxy$",
"^setWindowRect$",
"^strictFileInteractability$",
"^timeouts$",
"^unhandledPromptBehavior$",
"^webSocketUrl$") // from webdriver-bidi
.map(Pattern::compile)
.map(Pattern::asPredicate)
.reduce(identity -> false, Predicate::or);
// "selenoid:options" -> true, matched by the first pattern
// "screenResolution" -> false, no colon and not a standard key
// ":foo" -> false, yet the spec allows it: it does contain a colon
// "foo.bar:baz" -> false, '.' is not a word character
// "userAgent" -> false, a standard key with no pattern of its owngo deeper
Know that a capability key with a colon in it is an extension capability and that plain non-standard names are not safe to send. Be able to point at the colon as the thing that makes the difference.
Be ready to walk the validation switch in order and say why the extension-capability arm always matches before the rejection arm — and to know it carries nine named standard arms, not one per table row. Naming Selenium's accepted-key predicate and its leading prefix pattern is a strong addition.
Be ready to explain that the rejection arm binds an endpoint node specifically, and to design around the pass-through arm's silence by asserting that a namespaced setting actually took effect rather than trusting a clean session start. Be ready too to say where a client's own key filter is stricter than the protocol it speaks.
Be ready to set a house rule for how teams spell settings aimed at anything between the client and the browser, and to judge when a leniently-accepted bare key is a liability worth banning outright in review.
## The closed table the colon works around A WebDriver `New Session` request carries a capabilities object, and the specification publishes a fixed **table of standard capabilities** — the browser, the platform, the page load strategy, the proxy configuration and a few more. That table is **closed**. Anyone wanting a setting of their own — a browser vendor, a grid you run, a hosted provider — has nowhere legal to put it. The specification's answer is the **extension capability**: an extra capability whose key *must contain a `:` (colon) character*, denoting an implementation-specific namespace. The value can be arbitrary JSON, and the text before the colon is only a *suggested* CSS vendor keyword — which is why real namespaces read like short owner names. ## What the validation algorithm actually does The spec's *validate capabilities* algorithm runs an ordered switch over each key of `alwaysMatch` and every `firstMatch` entry: - A `null` value deserializes to `null`, and is then dropped from the validated object entirely. - Nine arms each name one standard key and type-check or deserialize it: `acceptInsecureCerts`, `browserName`, `browserVersion`, `platformName`, `pageLoadStrategy`, `proxy`, `strictFileInteractability`, `timeouts`, `unhandledPromptBehavior`. A wrong type is `invalid argument`. - A name that is an **additional WebDriver capability**, defined by *another* specification, runs that specification's deserialization algorithm. - A name that is **the key of an extension capability** — a name containing a colon — is deserialized in an implementation-specific way *if the implementation knows it*, and **otherwise `deserialized` is set to the value unchanged**. - Only the final arm, *"the remote end is an endpoint node"*, returns `invalid argument`. Read the last two arms together and the asymmetry falls out: a colon-bearing key never reaches the rejection arm, because the extension-capability arm matches first and always produces a value. A bare key with no arm of its own falls through, and there an endpoint node must reject it. Resist the shortcut "in the standard table, therefore safe". Two of the table's keys — `setWindowRect` and `userAgent` — have no arm here at all: they live in the table and in the *matched* capabilities a remote end builds for its reply, never in the object it validates. **Requesting** either falls through to the same final arm as an invented bare name. An arm buys passage, not table membership. ## A precision worth carrying That last arm is conditioned on the remote end being an **endpoint node** — the thing that actually owns the browser. An **intermediary node**, the spec's term for a hop that routes a session onward, is not obliged by it to reject an unknown bare key. So "bare keys are rejected" is exactly true of the browser end and only conventionally true of everything in front of it. ## The client refuses before the wire, and refuses more Selenium's Java client encodes a **narrower** rule. `AcceptedW3CCapabilityKeys` reduces a list of regular expressions with `or`, and the first is `^[\w-]+:.*$` — which the Java source must spell with the backslash doubled, since `\w` alone is not a legal escape in a literal. Every other pattern is an exact anchored match on one key, so it accepts: 1. a key whose colon is preceded by one or more ASCII word or hyphen characters and nothing else; 2. ten standard keys, spelled exactly — there is no `^userAgent$` pattern, so that one is refused locally; 3. `webSocketUrl`, admitted separately as the BiDi opt-in. Point 1 is **not** the spec's rule. The spec asks only that a key *contain* a colon, and constrains the prefix not at all. Run that pattern list: `:foo`, `foo.bar:baz`, `a+b:c` and any non-ASCII prefix all come back false — each a legal extension capability this client will not send. A client may be stricter than the protocol it speaks. `NewSessionPayload` runs that predicate and throws `IllegalArgumentException` with the message *"Illegal key values seen in w3c capabilities"* listing the offenders, so a refused key fails locally with a readable error. `RemoteWebDriver` separately logs a warning naming non-compliant keys. Selenoid — unmaintained, per its own README — documents this pressure: it accepts `screenResolution` loose *and* inside `selenoid:options`, because some clients carry only spec-legal keys. ## What it means for a suite Take a school-timetabling suite that needs a wider screen so its weekly timetable is not clipped. Two spellings behave differently: - Bare `screenResolution` beside `browserName`: legal only if every hop is lenient. A strict client throws before sending; an endpoint node answers `invalid argument`. - The same setting inside a namespaced map with an ordinary short prefix: accepted by the spec's algorithm and a client's filter alike, and untouched by any hop that does not own the namespace. ## The cost of the same rule The pass-through arm has no vocabulary check. Nothing in the algorithm knows which keys a namespace defines, so a misspelling inside one is carried, ignored and never reported: - A namespace prefix with two letters transposed is still a syntactically perfect extension capability, and means nothing to anybody. - A correct namespace with a mistyped inner key is equally silent. - The session starts, the setting does nothing, and the only symptom is behaviour you did not ask for. That silence is the price of extensibility: the colon tells every hop *"not yours, do not judge it"*, and every hop obeys — including the one that would have caught the typo.
- A key inside your namespaced map is misspelled. What does the remote end report?Nothing. The validation algorithm's extension-capability arm keeps an unrecognised namespaced value unchanged and never inspects what is inside it, so a mistyped inner key is forwarded or dropped silently by whichever component owns the namespace. The session is created normally and the only symptom is the setting not taking effect, which is why namespaced settings deserve an explicit assertion in the run rather than trust.
- Where does Selenium's client stop a bare non-standard capability key?In `NewSessionPayload`'s validation, which filters every merged key through `AcceptedW3CCapabilityKeys` and throws `IllegalArgumentException` listing the illegal keys before the request is written. `RemoteWebDriver` also logs a warning naming non-compliant keys. That filter is the client's own and is narrower than the specification — it insists on at least one ASCII word or hyphen character before the colon, which the spec does not — so a key it refuses is not necessarily one a remote end would have refused. Both happen locally, so you get a readable client-side failure rather than a remote `invalid argument` whose message may be much less specific.
- Does the colon rule say anything about what the prefix should be?Only as a suggestion. The specification requires the key to contain a colon and treats the text before the first colon as an implementation-specific namespace; it then suggests that this text be based on the CSS vendor keywords. Nothing in the specification validates the prefix, so an unregistered or invented namespace is just as legal on the wire as a well-known one — though a client library may apply a stricter filter of its own before sending.
saying these in an interview costs you the question
- Claims any unknown capability key is rejected outright
- Thinks the colon is a convention rather than a requirement
- Believes the remote end validates what is inside the namespace
- Says a namespaced typo produces an error message
- Assumes every hop refuses a bare key identically
- Reads Selenium's key predicate as the specification's own rule