Skip to content

.pr_agent_auto_best_practices

root edited this page Jun 29, 2025 · 8 revisions

Pattern 1: Add null checks and validation for parameters, properties, and return values before using them to prevent NullReferenceException and other runtime errors. This includes checking dictionary keys exist before accessing them, validating required JSON properties, and ensuring objects are not null before method calls.

Example code before:

var result = response.Value.ToString();
var item = dictionary[key];
handler.Invoke(args);

Example code after:

var result = response.Value?.ToString() ?? "default";
if (dictionary.TryGetValue(key, out var item)) { /* use item */ }
ArgumentNullException.ThrowIfNull(handler);
handler.Invoke(args);
Relevant past accepted suggestions:
Suggestion 1:

Add fallback for None reason

The response.reason might be None in some cases, which would result in "None" being displayed. Add a fallback to handle cases where the reason is not available.

py/selenium/webdriver/remote/remote_connection.py [443]

-return {"status": statuscode, "value": f"{response.reason}" if not data else data.strip()}
+return {"status": statuscode, "value": f"{response.reason or statuscode}" if not data else data.strip()}

Suggestion 2:

Add missing null check validation

Add null check for clientConfig parameter to maintain consistency with other parameter validations. The method handles null options and service but doesn't validate clientConfig which could cause issues downstream.

java/src/org/openqa/selenium/ie/InternetExplorerDriver.java [111-119]

 public InternetExplorerDriver(
     @Nullable InternetExplorerDriverService service,
     @Nullable InternetExplorerOptions options,
     @Nullable ClientConfig clientConfig) {
   if (options == null) {
     options = new InternetExplorerOptions();
   }
   if (service == null) {
     service = InternetExplorerDriverService.createDefaultService();
+  }
+  if (clientConfig == null) {
+    clientConfig = ClientConfig.defaultConfig();
+  }

Suggestion 3:

Validate required JSON fields

The from_json method should validate that required fields (realm, origin, type) are present in the JSON data. Currently, it allows None values for required fields which could cause issues downstream.

py/selenium/webdriver/common/bidi/script.py [57-75]

 @classmethod
 def from_json(cls, json: dict) -> "RealmInfo":
     """Creates a RealmInfo instance from a dictionary.
 
     Parameters:
     -----------
         json: A dictionary containing the realm information.
 
     Returns:
     -------
         RealmInfo: A new instance of RealmInfo.
     """
+    if not json.get("realm") or not json.get("origin") or not json.get("type"):
+        raise ValueError("Required fields 'realm', 'origin', and 'type' must be present")
+    
     return cls(
         realm=json.get("realm"),
         origin=json.get("origin"),
         type=json.get("type"),
         context=json.get("context"),
         sandbox=json.get("sandbox"),
     )

Suggestion 4:

Prevent potential KeyError

**params))

  •        del self.subscriptions[event_name]
    
  •    if event_name in self.subscriptions:
    
  •        callbacks = self.subscriptions[event_name]
    
  •        if callback_id in callbacks:
    
  •            callbacks.remove(callback_id)
    
  •            if not callbacks:
    
  •                params = {"events": [event_name]}
    
  •                session = Session(self.conn)
    
  •                self.conn.execute(session.unsubscribe(**params))
    
  •                del self.subscriptions[event_name]
    







**The code attempts to access self.subscriptions[event_name] after checking if event_name is in self.subscriptions, but then immediately accesses it again on the next line without checking. This could cause a KeyError if event_name is not in self.subscriptions.**

[py/selenium/webdriver/common/bidi/browsing_context.py [716-718]](https://github.com/SeleniumHQ/selenium/pull/15848/files#diff-a65d02c951aeb477672aa52e5eb0106645ba869aa186c0af6d2672c42cac95c6R716-R718)

```diff
 +        self.conn.remove_callback(event_obj, callback_id)
 +        if event_name in self.subscriptions and callback_id in self.subscriptions[event_name]:
 +            self.subscriptions[event_name].remove(callback_id)
++            if len(self.subscriptions[event_name]) == 0:
++                params = {"events": [event_name]}
++                session = Session(self.conn)
++                self.conn.execute(session.unsubscribe(**params))
++                del self.subscriptions[event_name]

Suggestion 5:

Prevent ValueError on removal

Check if the callback_id exists in the subscriptions before attempting to remove it. The current implementation will raise a ValueError if the callback_id is not in the list, which could happen if it was already removed or never added.

py/selenium/webdriver/common/bidi/browsing_context.py [716]

 def remove_event_handler(self, event: str, callback_id: int) -> None:
     """Remove an event handler from the browsing context.
 
     Parameters:
     ----------
         event: The event to unsubscribe from.
         callback_id: The callback id to remove.
     """
     try:
         event_name = self.EVENTS[event]
     except KeyError:
         raise Exception(f"Event {event} not found")
 
     event_obj = BrowsingContextEvent(event_name)
 
     self.conn.remove_callback(event_obj, callback_id)
-    self.subscriptions[event_name].remove(callback_id)
+    if event_name in self.subscriptions and callback_id in self.subscriptions[event_name]:
+        self.subscriptions[event_name].remove(callback_id)

Suggestion 6:

Check dictionary key exists

The code doesn't check if the successId exists in the _pendingCommands dictionary before accessing it. This could lead to a KeyNotFoundException if a success message is received for a command ID that is not in the dictionary.

dotnet/src/webdriver/BiDi/Communication/Broker.cs [136-142]

 try
 {
     var data = await _transport.ReceiveAsync(cancellationToken).ConfigureAwait(false);
 
     Utf8JsonReader utfJsonReader = new(new ReadOnlySpan<byte>(data));
     utfJsonReader.Read();
     var messageType = utfJsonReader.GetDiscriminator("type");
 
     switch (messageType)
     {
         case "success":
             var successId = int.Parse(utfJsonReader.GetDiscriminator("id"));
-            var successCommand = _pendingCommands[successId];
-            var messageSuccess = JsonSerializer.Deserialize(ref utfJsonReader, successCommand.Item1.ResultType, _jsonSerializerContext);
+            if (_pendingCommands.TryGetValue(successId, out var successCommand))
+            {
+                var messageSuccess = JsonSerializer.Deserialize(ref utfJsonReader, successCommand.Item1.ResultType, _jsonSerializerContext);
 
-            successCommand.Item2.SetResult(messageSuccess);
+                successCommand.Item2.SetResult(messageSuccess);
 
-            _pendingCommands.TryRemove(successId, out _);
+                _pendingCommands.TryRemove(successId, out _);
+            }
             break;

Suggestion 7:

Check event handlers exist

The code doesn't check if the method exists in the _eventHandlers dictionary or if there are any handlers for that method before accessing it. This could lead to a KeyNotFoundException or InvalidOperationException (when calling First() on an empty collection) if an event is received for a method that has no registered handlers.

dotnet/src/webdriver/BiDi/Communication/Broker.cs [145-159]

 case "event":
     utfJsonReader.Read();
     utfJsonReader.Read();
     var method = utfJsonReader.GetString();
 
     utfJsonReader.Read();
 
-    // TODO: Just get type info from existing subscribers, should be better
-    var type = _eventHandlers[method].First().EventArgsType;
+    if (_eventHandlers.TryGetValue(method, out var handlers) && handlers.Count > 0)
+    {
+        // TODO: Just get type info from existing subscribers, should be better
+        var type = handlers.First().EventArgsType;
 
-    var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref utfJsonReader, type, _jsonSerializerContext);
+        var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref utfJsonReader, type, _jsonSerializerContext);
 
-    var messageEvent = new MessageEvent(method, eventArgs);
-    _pendingEvents.Add(messageEvent);
+        var messageEvent = new MessageEvent(method, eventArgs);
+        _pendingEvents.Add(messageEvent);
+    }
     break;

Suggestion 8:

Validate discriminator existence

The method doesn't handle the case where the discriminator property isn't found in the JSON object. If the property doesn't exist, the method will return null, which could lead to unexpected behavior in the converters that use this method. Consider adding validation to throw a more descriptive exception when the discriminator property is missing.

dotnet/src/webdriver/BiDi/Communication/Json/Internal/JsonExtensions.cs [26-52]

 public static string? GetDiscriminator(this ref Utf8JsonReader reader, string name)
 {
     Utf8JsonReader readerClone = reader;
 
     if (readerClone.TokenType != JsonTokenType.StartObject)
         throw new JsonException();
 
     string? discriminator = null;
 
     readerClone.Read();
     while (readerClone.TokenType == JsonTokenType.PropertyName)
     {
         string? propertyName = readerClone.GetString();
         readerClone.Read();
 
         if (propertyName == name)
         {
             discriminator = readerClone.GetString();
             break;
         }
 
         readerClone.Skip();
         readerClone.Read();
     }
 
+    if (discriminator == null)
+        throw new JsonException($"Required discriminator property '{name}' not found in JSON object.");
+
     return discriminator;
 }

Suggestion 9:

Handle potential null string

The RemoteValue.String() method is called with a potentially null string value from GetString(), but the method doesn't appear to handle null values properly. This could lead to a NullReferenceException.

dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RemoteValueConverter.cs [36]

-return RemoteValue.String(jsonDocument.RootElement.GetString());
+return RemoteValue.String(jsonDocument.RootElement.GetString() ?? string.Empty);

Suggestion 10:

Add null check for handler

Add null check for the returned HttpClientHandler from CreateHttpClientHandler() before using it to avoid potential NullReferenceException

dotnet/src/webdriver/Remote/HttpCommandExecutor.cs [248-252]

 protected virtual HttpClient CreateHttpClient()
 {
     var httpClientHandler = CreateHttpClientHandler();
+    if (httpClientHandler == null)
+    {
+        throw new InvalidOperationException("CreateHttpClientHandler() returned null");
+    }
 
     HttpMessageHandler handler = httpClientHandler;

Suggestion 11:

Add null validation check for required class field to prevent potential NullReferenceException

The CreateHttpClientHandler() method uses this.remoteServerUri without validating that it's not null. Since this is a required field for the class functionality, add a null check at the start of the method to fail fast with a clear error message.

dotnet/src/webdriver/Remote/HttpCommandExecutor.cs [229-233]

 protected virtual HttpClientHandler CreateHttpClientHandler()
 {
+    ArgumentNullException.ThrowIfNull(this.remoteServerUri, nameof(remoteServerUri));
     HttpClientHandler httpClientHandler = new HttpClientHandler();
     string userInfo = this.remoteServerUri.UserInfo;
     if (!string.IsNullOrEmpty(userInfo) && userInfo.Contains(":"))

Suggestion 12:

Add null checks for nullable delegates

The FindElementMethod and FindElementsMethod properties are marked as nullable but used without null checks in FindElement and FindElements methods. Add null checks to prevent potential NullReferenceException.

dotnet/src/webdriver/By.cs [332-333]

 public virtual IWebElement FindElement(ISearchContext context)
 {
+    if (this.FindElementMethod == null)
+    {
+        throw new InvalidOperationException("FindElementMethod not set");
+    }
     return this.FindElementMethod(context);
 }

Suggestion 13:

Add null validation checks for nullable delegate properties before invoking them to prevent NullReferenceExceptions

The FindElement and FindElements methods should validate that their FindElementMethod and FindElementsMethod properties are not null before using them, since they are marked as nullable. Add ArgumentNullException checks at the start of these methods.

dotnet/src/webdriver/By.cs [330-344]

 public virtual IWebElement FindElement(ISearchContext context)
 {
+    ArgumentNullException.ThrowIfNull(this.FindElementMethod, nameof(FindElementMethod));
     return this.FindElementMethod(context);
 }
 
-public virtual ReadOnlyCollection<IWebElement> FindElements(ISearchContext context) 
+public virtual ReadOnlyCollection<IWebElement> FindElements(ISearchContext context)
 {
+    ArgumentNullException.ThrowIfNull(this.FindElementsMethod, nameof(FindElementsMethod)); 
     return this.FindElementsMethod(context);
 }

Suggestion 14:

Add parameter validation

Add null checks for the subscription ID and event handler parameters to prevent potential NullReferenceException when unsubscribing.

dotnet/src/webdriver/BiDi/Communication/Broker.cs [274-281]

 public async Task UnsubscribeAsync(Modules.Session.Subscription subscription, EventHandler eventHandler)
 {
+    ArgumentNullException.ThrowIfNull(subscription);
+    ArgumentNullException.ThrowIfNull(eventHandler);
+    
     var eventHandlers = _eventHandlers[eventHandler.EventName];
     eventHandlers.Remove(eventHandler);
     await _bidi.SessionModule.UnsubscribeAsync([subscription]).ConfigureAwait(false);
 }

Suggestion 15:

Validate JSON property existence

Add null check for the 'clientWindows' property existence in JSON to prevent potential JsonException

dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetClientWindowsResultConverter.cs [35]

-var clientWindows = doc.RootElement.GetProperty("clientWindows").Deserialize<IReadOnlyList<ClientWindowInfo>>(options);
+if (!doc.RootElement.TryGetProperty("clientWindows", out JsonElement clientWindowsElement))
+    throw new JsonException("Missing required 'clientWindows' property");
+var clientWindows = clientWindowsElement.Deserialize<IReadOnlyList<ClientWindowInfo>>(options);

Suggestion 16:

Add null check for response value

Add null check for response.Value in GetContext() method before attempting ToString() conversion to avoid potential NullReferenceException.

dotnet/src/webdriver/Firefox/FirefoxDriver.cs [263-266]

 Response commandResponse = this.Execute(GetContextCommand, null);
 
-if (commandResponse.Value is not string response
+if (commandResponse.Value is null
+    || commandResponse.Value is not string response
     || !Enum.TryParse(response, ignoreCase: true, out FirefoxCommandContext output))

Pattern 2: Fix string validation logic when checking sequences by excluding strings first before checking if the value is a sequence, since strings are sequences in Python but shouldn't be treated as argument collections.

Example code before:

if not isinstance(value, Sequence) or isinstance(value, str):
    raise TypeError("must be a sequence")
self._args = value

Example code after:

if isinstance(value, str) or not isinstance(value, Sequence):
    raise TypeError("must be a sequence")
self._args = list(value)
Relevant past accepted suggestions:
Suggestion 1:

Fix string validation logic

The validation logic incorrectly rejects strings because strings are sequences in Python. The condition should use isinstance(value, str) first to exclude strings, then check if it's a sequence. This prevents valid non-string sequences from being rejected.

py/selenium/webdriver/chrome/service.py [61-65]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
-    if not isinstance(value, Sequence) or isinstance(value, str):
+    if isinstance(value, str) or not isinstance(value, Sequence):
         raise TypeError("service_args must be a sequence")
     self._service_args = list(value)

Suggestion 2:

Convert sequence to list consistently

The setter should convert the sequence to a list to maintain consistency with the internal storage format. The current implementation stores the sequence directly, which could lead to issues if a non-list sequence is provided.

py/selenium/webdriver/chromium/service.py [75-79]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
     if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 3:

Add string exclusion validation consistency

The setter validation is inconsistent with other service classes. It should also exclude strings from being accepted as valid sequences, since strings are sequences but not the intended type for service arguments.

py/selenium/webdriver/chrome/service.py [61-65]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
-    if not isinstance(value, Sequence):
+    if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 4:

Convert sequence to list consistently

The setter should convert the sequence to a list to maintain consistency with the internal storage format. The current implementation directly assigns the sequence, which could lead to type inconsistencies since _service_args is expected to be a list.

py/selenium/webdriver/chromium/service.py [75-79]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
     if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 5:

Convert sequence to list assignment

Convert the sequence to a list before assignment to maintain consistency with the internal storage format and avoid potential issues with immutable sequences

py/selenium/webdriver/edge/service.py [64-68]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
     if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 6:

Add string exclusion validation consistency

The setter validation is inconsistent with other service classes. It should also exclude strings from being accepted as valid sequences, since strings are sequences but not the intended type.

py/selenium/webdriver/chrome/service.py [61-65]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
-    if not isinstance(value, Sequence):
+    if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 7:

Improve validation and conversion consistency

The setter validation should exclude strings and convert the sequence to a list for consistency with other service classes and internal storage format.

py/selenium/webdriver/edge/service.py [64-68]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
-    if not isinstance(value, Sequence):
+    if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 8:

Convert sequence to list

Convert the value to a list before assignment to ensure the internal storage is mutable and consistent with the initialization pattern used elsewhere in the codebase.

py/selenium/webdriver/chromium/service.py [77-79]

 if not isinstance(value, Sequence) or isinstance(value, str):
     raise TypeError("service_args must be a sequence")
-self._service_args = value
+self._service_args = list(value)

Suggestion 9:

Ensure service args mutability

Convert the input sequence to a list to ensure mutability for operations like append() used elsewhere in the code. This prevents potential issues when immutable sequences are passed.

py/selenium/webdriver/chromium/service.py [48]

-self._service_args = service_args or []
+self._service_args = list(service_args or [])

Suggestion 10:

Prevent strings as sequence arguments

The type check should exclude strings since they are sequences but shouldn't be treated as argument lists. Add a check to prevent strings from being accepted as valid sequences.

py/selenium/webdriver/chromium/service.py [75-79]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
-    if not isinstance(value, Sequence):
+    if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
     self._service_args = value

Pattern 3: Convert sequences to lists when storing them internally to ensure mutability and consistency, especially when the stored collection needs to support operations like append() or when maintaining a consistent internal storage format.

Example code before:

self._service_args = service_args or []
self._service_args = value

Example code after:

self._service_args = list(service_args or [])
self._service_args = list(value)
Relevant past accepted suggestions:
Suggestion 1:

Convert sequence to list consistently

The setter should convert the sequence to a list to maintain consistency with the internal storage format. The current implementation stores the sequence directly, which could lead to issues if a non-list sequence is provided.

py/selenium/webdriver/chromium/service.py [75-79]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
     if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 2:

Convert sequence to list consistently

The setter should convert the sequence to a list to maintain consistency with the internal storage format. The current implementation directly assigns the sequence, which could lead to type inconsistencies since _service_args is expected to be a list.

py/selenium/webdriver/chromium/service.py [75-79]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
     if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 3:

Convert sequence to list assignment

Convert the sequence to a list before assignment to maintain consistency with the internal storage format and avoid potential issues with immutable sequences

py/selenium/webdriver/edge/service.py [64-68]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
     if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 4:

Improve validation and conversion consistency

The setter validation should exclude strings and convert the sequence to a list for consistency with other service classes and internal storage format.

py/selenium/webdriver/edge/service.py [64-68]

 @service_args.setter
 def service_args(self, value: Sequence[str]):
-    if not isinstance(value, Sequence):
+    if not isinstance(value, Sequence) or isinstance(value, str):
         raise TypeError("service_args must be a sequence")
-    self._service_args = value
+    self._service_args = list(value)

Suggestion 5:

Convert sequence to list

Convert the value to a list before assignment to ensure the internal storage is mutable and consistent with the initialization pattern used elsewhere in the codebase.

py/selenium/webdriver/chromium/service.py [77-79]

 if not isinstance(value, Sequence) or isinstance(value, str):
     raise TypeError("service_args must be a sequence")
-self._service_args = value
+self._service_args = list(value)

Suggestion 6:

Ensure service args mutability

Convert the input sequence to a list to ensure mutability for operations like append() used elsewhere in the code. This prevents potential issues when immutable sequences are passed.

py/selenium/webdriver/chromium/service.py [48]

-self._service_args = service_args or []
+self._service_args = list(service_args or [])

Pattern 4: Fix incorrect Unicode key mappings and event subscription names by ensuring the correct values are used for key codes and event names, particularly for platform-specific keys and WebDriver BiDi events.

Example code before:

options: "\ue050", # macOS Options key, same as right_shift
await Broker.SubscribeAsync("browsingContext.fragmentNavigated", handler)

Example code after:

options: "\ue052", # macOS Options key, same as right_alt
await Broker.SubscribeAsync("browsingContext.historyUpdated", handler)
Relevant past accepted suggestions:
Suggestion 1:

Fix incorrect macOS key mapping

The key mappings appear incorrect for macOS. The Options key should map to Alt/Option functionality, not right_shift, and the Function key has its own distinct behavior that shouldn't be aliased to right_control.

rb/lib/selenium/webdriver/common/keys.rb [98-99]

-options: "\ue050", # macOS Options key, same as right_shift
+options: "\ue052", # macOS Options key, same as right_alt
 function: "\ue051", # macOS Function key, same as right_control

Suggestion 2:

Fix duplicate Unicode key mappings

The OPTIONS and FUNCTION keys use Unicode values that conflict with the right-side modifier keys defined above. This creates duplicate Unicode mappings which could cause unexpected behavior when these keys are used.

py/selenium/webdriver/common/keys.py [99-100]

-OPTIONS = "\uE050" # TODO: verify Unicode value with WebDriver spec
-FUNCTION = "\uE051" # TODO: symbolic only; confirm or remove in future
+OPTIONS = "\uE054" # TODO: verify Unicode value with WebDriver spec
+FUNCTION = "\uE055" # TODO: symbolic only; confirm or remove in future

Suggestion 3:

Fix incorrect event subscription name

The OnHistoryUpdatedAsync methods are subscribing to the wrong event name. They should subscribe to "browsingContext.historyUpdated" instead of "browsingContext.fragmentNavigated" to match their intended functionality.

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs [137-145]

 public async Task<Subscription> OnHistoryUpdatedAsync(Func<HistoryUpdatedEventArgs, Task> handler, BrowsingContextsSubscriptionOptions? options = null)
 {
-    return await Broker.SubscribeAsync("browsingContext.fragmentNavigated", handler, options).ConfigureAwait(false);
+    return await Broker.SubscribeAsync("browsingContext.historyUpdated", handler, options).ConfigureAwait(false);
 }
 
 public async Task<Subscription> OnHistoryUpdatedAsync(Action<HistoryUpdatedEventArgs> handler, BrowsingContextsSubscriptionOptions? options = null)
 {
-    return await Broker.SubscribeAsync("browsingContext.fragmentNavigated", handler, options).ConfigureAwait(false);
+    return await Broker.SubscribeAsync("browsingContext.historyUpdated", handler, options).ConfigureAwait(false);
 }

Suggestion 4:

Verify Unicode key code standards

The Unicode code points \uE050 and \uE051 appear to be in the Private Use Area which may not be standardized across WebDriver implementations. Consider using established WebDriver protocol key codes or verify these values align with W3C WebDriver specification for macOS keys.

java/src/org/openqa/selenium/Keys.java [101-102]

-OPTION('\uE050'),
-FN('\uE051'),
+OPTION('\uE050'), // TODO: Verify Unicode value with WebDriver spec
+FN('\uE051'),     // TODO: Verify Unicode value with WebDriver spec

Pattern 5: Ensure proper resource cleanup by closing sockets, processes, and drivers in finally blocks or using context managers, and check process state before attempting to terminate to prevent resource leaks and exceptions.

Example code before:

sock = socket.socket()
sock.connect((host, port))
driver = webdriver.Chrome()
driver.get(url)
driver.quit()

Example code after:

sock = socket.socket()
try:
    sock.connect((host, port))
finally:
    sock.close()

driver = webdriver.Chrome()
try:
    driver.get(url)
finally:
    driver.quit()
Relevant past accepted suggestions:
Suggestion 1:

Add error handling

Add error handling to the kill method similar to what was added to terminate. This ensures consistent behavior when trying to kill a process that no longer exists.

rb/lib/selenium/webdriver/common/child_process.rb [129-131]

 def kill(pid)
   Process.kill(SIGKILL, pid)
+rescue Errno::ECHILD, Errno::ESRCH
+  # Process does not exist, nothing to kill
 end

Suggestion 2:

Ensure proper resource cleanup

Ensure the driver is properly closed even if the test fails by using a try-finally block. Currently, if the test fails before reaching driver.quit(), the driver won't be properly cleaned up.

py/test/selenium/webdriver/remote/remote_connection_tests.py [38-53]

 def test_remote_webdriver_with_http_timeout(firefox_options, webserver):
     """This test starts a remote webdriver with an http client timeout
     set less than the implicit wait timeout, and verifies the http timeout
     is triggered first when waiting for an element.
     """
     http_timeout = 6
     wait_timeout = 8
     server_addr = f"http://{webserver.host}:{webserver.port}"
     client_config = ClientConfig(remote_server_addr=server_addr, timeout=http_timeout)
     assert client_config.timeout == http_timeout
     driver = webdriver.Remote(options=firefox_options, client_config=client_config)
-    driver.get(f"{server_addr}/simpleTest.html")
-    driver.implicitly_wait(wait_timeout)
-    with pytest.raises(ReadTimeoutError):
-        driver.find_element(By.ID, "no_element_to_be_found")
-    driver.quit()
+    try:
+        driver.get(f"{server_addr}/simpleTest.html")
+        driver.implicitly_wait(wait_timeout)
+        with pytest.raises(ReadTimeoutError):
+            driver.find_element(By.ID, "no_element_to_be_found")
+    finally:
+        driver.quit()

Suggestion 3:

Close socket to prevent leaks

The socket is not being closed after use, which can lead to resource leaks. Always close sockets after use, preferably using a context manager or explicitly calling close().

py/selenium/webdriver/remote/server.py [120-125]

 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 host = self.host if self.host is not None else "localhost"
 try:
     sock.connect((host, self.port))
+    sock.close()
     raise ConnectionError(f"Selenium server is already running, or something else is using port {self.port}")
 except ConnectionRefusedError:
+    sock.close()

Suggestion 4:

Check process state before terminating

The stop() method should be more resilient by checking if the process is still running before attempting to terminate it. The current implementation could raise an exception if the process has already terminated.

py/selenium/webdriver/remote/server.py [134-142]

 def stop(self):
     """Stop the server."""
     if self.process is None:
         raise RuntimeError("Selenium server isn't running")
     else:
-        self.process.terminate()
-        self.process.wait()
+        if self.process.poll() is None:  # Check if process is still running
+            self.process.terminate()
+            self.process.wait()
         self.process = None
         print("Selenium server has been terminated")

Suggestion 5:

Missing driver cleanup

The test is missing cleanup for driver2. If driver2 is successfully created, it should be quit in the finally block to properly release resources.

py/test/selenium/webdriver/chrome/chrome_service_tests.py [53-54]

 finally:
     driver1.quit()
+    if driver2:
+        driver2.quit()

Suggestion 6:

Handle undefined variable safely

The test is missing a reference to driver1 before quitting it in the finally block. Since driver1 is defined inside the try block, it might not exist if an exception occurs before its creation, causing a NameError.

py/test/selenium/webdriver/chrome/chrome_service_tests.py [54]

-driver1.quit()
+if 'driver1' in locals() and driver1:
+    driver1.quit()

Suggestion 7:

Fix DevTools connections closure

The current implementation of the quit method might not properly close all DevTools connections if @devtools is initialized but empty. Use each instead of map to ensure proper closure of all connections.

rb/lib/selenium/webdriver/common/driver.rb [187-192]

 def quit
   bridge.quit
 ensure
   @service_manager&.stop
-  @devtools&.values&.map(&:close)
+  @devtools&.values&.each(&:close) if @devtools
 end

[Auto-generated best practices - 2025-06-29]

Clone this wiki locally