#18085·selenium

[ Bug]: [py] browser.set_client_window_state nests the rect state under "state" instead of sending sibling params

Author: AutomatedTesterCreated Sep 28, 2026Updated Sep 28, 2026

Description

driver.browser.set_client_window_state() builds a non-conformant wire message whenever the window state is given as a rect rather than a named string. It nests the whole rect state under the state key, where the spec requires state to be the string "normal" and width / height / x / y to be siblings of clientWindow in the params map.

Because the rect object is the only way to pass a size or position, there is currently no working way to set a client window rect from Python.

Root cause

The spec splices the variant into the enclosing params map with a CDDL group choice, so the variant's members are siblings, not a nested value:

cddl
browser.SetClientWindowStateParameters = {
  clientWindow: browser.ClientWindow,
  (browser.ClientWindowNamedState // browser.ClientWindowRectState)
}

browser.ClientWindowNamedState = (
  state: "fullscreen" / "maximized" / "minimized"
)

browser.ClientWindowRectState = (
  state: "normal",
  ? width: js-uint,
  ? height: js-uint,
  ? x: js-int,
  ? y: js-int,
)

The hand-written method treats the group choice as a nested object instead — py/private/bidi_enhancements_manifest.py:186-199, emitted to selenium/webdriver/common/bidi/browser.py:337:

python
state_param = state
if hasattr(state, '__dataclass_fields__'):
    state_param = {k: v for k, v in state.__dict__.items() if v is not None}

params = {
    "clientWindow": client_window,
    "state": state_param,     # <- the whole rect ends up here
}

Reproducible code

python
from selenium.webdriver.common.bidi.browser import ClientWindowRectState

windows = driver.browser.get_client_windows()
driver.browser.set_client_window_state(
    client_window=windows[0].client_window,
    state=ClientWindowRectState(width=800, height=600),
)

Sent:

json
{"clientWindow": "...", "state": {"state": "normal", "width": 800, "height": 600}}

Required:

json
{"clientWindow": "...", "state": "normal", "width": 800, "height": 600}

state is an object where the spec requires a string, so a conformant browser rejects the command with invalid argument.

A plain dict is documented as accepted too (state: ... - A dict representing the state) and fails the same way — it isn't a dataclass, so it is passed through and nested verbatim.

What does work

The named-string path is fine: state="fullscreen" / "maximized" / "minimized" produce {"clientWindow": ..., "state": "maximized"}.

state="normal" also works, and produces a valid ClientWindowRectState with every optional member omitted — it just can't carry a rect.

Notes

  • Untested. There is no coverage for set_client_window_state anywhere under py/test/, which is why this has gone unnoticed.
  • The generated low-level layer already models this correctly. selenium/webdriver/common/_bidi/browser.py emits SetClientWindowStateParametersClientWindowRectState with client_window, state (baked "normal", init=False), width, height, x, y all as siblings, and a Union discriminated on state. So the fix can either flatten the params by hand or delegate to _bidi, depending on where the #17670 transition lands.
  • Confirmed against the pinned CDDL in this repo (browser.SetClientWindowStateParameters). Not yet confirmed against a live browser — the wire shape is wrong regardless, but the exact error a given browser returns is unverified.

Cross-binding

Worth checking whether the other bindings flatten the group choice correctly before fixing only Python.