Skip to content

.pr_agent_accepted_suggestions

qodo-merge-bot edited this page Oct 15, 2025 · 163 revisions
                     PR 16427 (2025-10-14)                    
[possible issue] Remove global docstring check ignore

✅ Remove global docstring check ignore

Remove the global ignore rule for docstring (D) checks from pyproject.toml to ensure the linter can report docstring issues as intended.

py/pyproject.toml [173-174]

 [tool.ruff.lint.per-file-ignores]
-"*.py" = ["D"]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the per-file-ignores configuration completely negates the enabling of docstring checks, making the primary change of this PR ineffective.



                     PR 16421 (2025-10-13)                    
[general] Consolidate browser skip logic

✅ Consolidate browser skip logic

Consolidate the separate if conditions for skipping the test on Chrome and Edge into a single condition using in for better readability and maintainability.

py/test/selenium/webdriver/common/bidi_network_tests.py [104-107]

-if driver.caps["browserName"] == "chrome":
-    pytest.skip(reason="Request handlers don't yet work in Chrome when using classic navigation")
-if driver.caps["browserName"] == "edge":
-    pytest.skip(reason="Request handlers don't yet work in Edge when using classic navigation")
+if driver.caps["browserName"] in ("chrome", "edge"):
+    pytest.skip(reason="Request handlers don't yet work in Chrome/Edge when using classic navigation")

Suggestion importance[1-10]: 4

__

Why: The suggestion improves code conciseness and maintainability by consolidating two if statements into a single check, which is a good practice but has a low impact on functionality.



                     PR 16403 (2025-10-08)                    
[possible issue] Use a non-generic base class

✅ Use a non-generic base class

Modify _pendingCommands to use a non-generic base CommandInfo class. This will allow storing different command result types in the dictionary, improving type safety.

dotnet/src/webdriver/BiDi/Communication/Broker.cs [40]

-private readonly ConcurrentDictionary<long, CommandInfo<EmptyResult>> _pendingCommands = new();
+private readonly ConcurrentDictionary<long, CommandInfo> _pendingCommands = new();

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a type safety issue where _pendingCommands is constrained to CommandInfo, which forces an unsafe cast later. Proposing a non-generic base class is a valid and robust design pattern to solve this problem.



                     PR 16392 (2025-10-06)                    
[possible issue] Remove exception from unimplemented method

Remove exception from unimplemented method

Remove the NotImplementedException from the Initialize method in EmulationModule and other modules. If no initialization is needed, the method body should be empty to prevent runtime crashes.

dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs [93-96]

 protected internal override void Initialize(Broker broker)
 {
-    throw new NotImplementedException();
+    // No-op for this module.
 }

Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical bug introduced by the PR. The Initialize method will be called upon module creation, and throwing a NotImplementedException will cause a runtime crash, making the modules unusable.


[general] Register module-specific JSON serialization context

Register module-specific JSON serialization context

Implement the Initialize method in BrowsingContextModule to register a module-specific JsonSerializerContext. This aligns with the pattern in BrowserModule and is necessary for proper JSON serialization.

dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs [254-257]

 protected internal override void Initialize(Broker broker)
 {
-    
+    broker.ConfigureJsonContext(opts => opts.TypeInfoResolverChain.Add(BrowsingContextModuleJsonSerializerContext.Default));
 }

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly points out that the PR's refactoring goal is to register module-specific JSON contexts, which is missing for BrowsingContextModule. Implementing this is crucial for completing the refactoring and gaining the performance/AOT benefits of source-generated serialization.



                     PR 16386 (2025-10-05)                    
[possible issue] Fix incorrect enum serialization casing

✅ Fix incorrect enum serialization casing

The UseStringEnumConverter option serializes enums to PascalCase, but the BiDi specification requires lowercase. Remove this option and instead implement custom JsonConverters for each enum to ensure correct serialization.

dotnet/src/webdriver/BiDi/Communication/Json/BiDiJsonSerializerContext.cs [189]

-[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
+// This line should be removed.
+// [JsonSourceGenerationOptions(UseStringEnumConverter = true)]

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical issue where using UseStringEnumConverter would cause enum serialization to violate the WebDriver BiDi specification (PascalCase vs. lowercase), leading to communication failures.



                     PR 16364 (2025-10-01)                    
[general] Remove redundant and unused code

✅ Remove redundant and unused code

Remove the redundant file javascript/grid-ui/src/hooks/useTheme.tsx. Its logic is already implemented in javascript/grid-ui/src/contexts/ThemeContext.tsx, and it is not used by the application.

javascript/grid-ui/src/hooks/useTheme.tsx [1-61]

-// Licensed to the Software Freedom Conservancy (SFC) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The SFC licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
+// This file should be deleted.
 
-import { useState, useEffect } from 'react'
-import { lightTheme, darkTheme } from '../theme/themes'
-
-type ThemeMode = 'light' | 'dark' | 'system'
-
-export const useTheme = () => {
-  const [themeMode, setThemeMode] = useState<ThemeMode>('system')
-  const [systemPrefersDark, setSystemPrefersDark] = useState(false)
-
-  useEffect(() => {
-    if (typeof window !== 'undefined') {
-      const saved = localStorage.getItem('theme-mode') as ThemeMode
-      if (saved) setThemeMode(saved)
-      setSystemPrefersDark(window.matchMedia('(prefers-color-scheme: dark)').matches)
-    }
-  }, [])
-
-
-
-  useEffect(() => {
-    if (typeof window !== 'undefined') {
-      localStorage.setItem('theme-mode', themeMode)
-    }
-  }, [themeMode])
-
-  useEffect(() => {
-    if (typeof window !== 'undefined') {
-      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
-      const handler = (e: MediaQueryListEvent) => setSystemPrefersDark(e.matches)
-      mediaQuery.addEventListener('change', handler)
-      return () => mediaQuery.removeEventListener('change', handler)
-    }
-  }, [])
-
-  const isDark = themeMode === 'dark' || (themeMode === 'system' && systemPrefersDark)
-  const currentTheme = isDark ? darkTheme : lightTheme
-
-  return {
-    themeMode,
-    setThemeMode,
-    currentTheme,
-    isDark
-  }
-}
-

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a redundant file (useTheme.tsx) whose logic is duplicated in ThemeContext.tsx. Removing this unused code improves maintainability and reduces confusion.


[general] Add assertions to validate behavior

✅ Add assertions to validate behavior

Add assertions to the 'cycles through theme modes on click' test in ThemeToggle.test.tsx. The test currently performs clicks without verifying the component's state changes.

javascript/grid-ui/src/tests/components/ThemeToggle.test.tsx [36-56]

 it('cycles through theme modes on click', () => {
   render(
     <CustomThemeProvider>
       <ThemeToggle />
     </CustomThemeProvider>
   )
-  
+
   const button = screen.getByRole('button')
-  
-  // Should start with system mode
-  expect(button).toHaveAttribute('aria-label', 'Toggle theme')
-  
-  // Click to light mode
+
+  // Starts in system mode, next is light
+  expect(screen.getByTestId('AutoModeIcon')).toBeInTheDocument()
   fireEvent.click(button)
-  
-  // Click to dark mode
+
+  // Now in light mode, next is dark
+  expect(screen.getByTestId('LightModeIcon')).toBeInTheDocument()
   fireEvent.click(button)
-  
-  // Click back to system mode
+
+  // Now in dark mode, next is system
+  expect(screen.getByTestId('DarkModeIcon')).toBeInTheDocument()
   fireEvent.click(button)
+
+  // Back to system mode
+  expect(screen.getByTestId('AutoModeIcon')).toBeInTheDocument()
 })

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the test case is missing assertions for intermediate states, which makes it ineffective. Improving the test coverage is a valuable enhancement.


[possible issue] Fix incorrect test mock setup

✅ Fix incorrect test mock setup

In TopBar.test.tsx, remove the incorrect mock for ../../hooks/useTheme. Instead, wrap the TopBar component with CustomThemeProvider to align the test environment with its actual usage.

javascript/grid-ui/src/tests/components/TopBar.test.tsx [23-43]

-// Mock the useTheme hook
-jest.mock('../../hooks/useTheme', () => ({
-  useTheme: () => ({
-    themeMode: 'light',
-    setThemeMode: jest.fn(),
-    currentTheme: {},
-    isDark: false
-  })
-}))
+import { CustomThemeProvider } from '../../contexts/ThemeContext'
 
 const user = userEvent.setup()
+
+const renderWithTheme = (ui: React.ReactElement) => {
+  return render(<CustomThemeProvider>{ui}</CustomThemeProvider>)
+}
 
 it('renders basic information', () => {
   const subheaderText = 'Hello, world!'
   const handleClick = jest.fn()
-  render(<TopBar subheader={subheaderText} drawerOpen
+  renderWithTheme(<TopBar subheader={subheaderText} drawerOpen
                   toggleDrawer={handleClick}/>)
   expect(screen.getByText('Selenium Grid')).toBeInTheDocument()
   expect(screen.getByRole('img')).toHaveAttribute('alt', 'Selenium Grid Logo')
   expect(screen.getByText(subheaderText)).toBeInTheDocument()
 })

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly points out that the test is mocking a redundant hook (useTheme) instead of using the CustomThemeProvider as the application does, leading to an incorrect test setup.


[learned best practice] Guard browser API accesses

✅ Guard browser API accesses

Add environment checks before using window, localStorage, and matchMedia to avoid runtime errors in SSR/tests and ensure safe access. Fallback gracefully when unavailable.

javascript/grid-ui/src/contexts/ThemeContext.tsx [40-55]

 useEffect(() => {
-  const saved = localStorage.getItem('theme-mode') as ThemeMode
-  if (saved) setThemeMode(saved)
-  setSystemPrefersDark(window.matchMedia('(prefers-color-scheme: dark)').matches)
+  if (typeof window !== 'undefined') {
+    try {
+      const saved = window.localStorage.getItem('theme-mode') as ThemeMode
+      if (saved) setThemeMode(saved)
+      const mq = window.matchMedia?.('(prefers-color-scheme: dark)')
+      setSystemPrefersDark(!!mq && mq.matches)
+    } catch {
+      // ignore storage access errors
+    }
+  }
 }, [])
 
 useEffect(() => {
-  localStorage.setItem('theme-mode', themeMode)
+  if (typeof window !== 'undefined') {
+    try {
+      window.localStorage.setItem('theme-mode', themeMode)
+    } catch {
+      // ignore storage access errors
+    }
+  }
 }, [themeMode])
 
 useEffect(() => {
-  const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
-  const handler = (e: MediaQueryListEvent) => setSystemPrefersDark(e.matches)
-  mediaQuery.addEventListener('change', handler)
-  return () => mediaQuery.removeEventListener('change', handler)
+  if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {
+    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
+    const handler = (e: MediaQueryListEvent) => setSystemPrefersDark(e.matches)
+    mediaQuery.addEventListener?.('change', handler)
+    return () => mediaQuery.removeEventListener?.('change', handler)
+  }
+  return
 }, [])

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Validate inputs and states early to prevent logic errors; guard access to browser-only APIs (window, localStorage, matchMedia) for non-browser/test environments.



                     PR 16362 (2025-10-01)                    
[possible issue] Fix syntax error in expression

✅ Fix syntax error in expression

Remove the extra closing curly brace } from the cache-key value to fix a syntax error in the GitHub Actions expression.

.github/workflows/ci-python.yml [70]

-cache-key: python-unit-test-${{ matrix.os }}}
+cache-key: python-unit-test-${{ matrix.os }}

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a syntax error in the GitHub Actions expression for cache-key which would cause the CI job to fail.



                     PR 16338 (2025-09-20)                    
[possible issue] Prevent resource leaks with caching

✅ Prevent resource leaks with caching

Cache the BiDi instance to prevent creating a new HTTP client and connection on every invocation. This avoids potential resource leaks and performance issues.

java/src/org/openqa/selenium/bidi/BiDiProvider.java [47-56]

-return () -> {
-  URI wsUri = getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));
-
-  HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
-  ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
-  HttpClient wsClient = clientFactory.createClient(wsConfig);
-  Connection connection = new Connection(wsClient, wsUri.toString());
-
-  return Optional.of(new BiDi(connection));
+return new HasBiDi() {
+  private volatile Optional<BiDi> cachedBiDi;
+  
+  @Override
+  public Optional<BiDi> getBiDi() {
+    if (cachedBiDi == null) {
+      synchronized (this) {
+        if (cachedBiDi == null) {
+          URI wsUri = getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));
+          HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
+          ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
+          HttpClient wsClient = clientFactory.createClient(wsConfig);
+          Connection connection = new Connection(wsClient, wsUri.toString());
+          cachedBiDi = Optional.of(new BiDi(connection));
+        }
+      }
+    }
+    return cachedBiDi;
+  }
 };

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a significant issue where new HTTP clients and connections are created on each call, which can lead to resource exhaustion and performance degradation. The proposed caching solution is a robust fix for this problem.


[possible issue] Prevent connection recreation with caching

✅ Prevent connection recreation with caching

Cache the DevTools instance to avoid recreating connections and performing version matching on every call. This prevents potential resource leaks and unnecessary overhead.

java/src/org/openqa/selenium/devtools/DevToolsProvider.java [45-53]

-return () -> {
-  Object cdpVersion = caps.getCapability("se:cdpVersion");
-  String version = cdpVersion instanceof String ? (String) cdpVersion : caps.getBrowserVersion();
-
-  CdpInfo info = new CdpVersionFinder().match(version).orElseGet(NoOpCdpInfo::new);
-  Optional<DevTools> devTools =
-    SeleniumCdpConnection.create(caps).map(conn -> new DevTools(info::getDomains, conn));
-  return devTools;
+return new HasDevTools() {
+  private volatile Optional<DevTools> cachedDevTools;
+  
+  @Override
+  public Optional<DevTools> getDevTools() {
+    if (cachedDevTools == null) {
+      synchronized (this) {
+        if (cachedDevTools == null) {
+          Object cdpVersion = caps.getCapability("se:cdpVersion");
+          String version = cdpVersion instanceof String ? (String) cdpVersion : caps.getBrowserVersion();
+          CdpInfo info = new CdpVersionFinder().match(version).orElseGet(NoOpCdpInfo::new);
+          cachedDevTools = SeleniumCdpConnection.create(caps).map(conn -> new DevTools(info::getDomains, conn));
+        }
+      }
+    }
+    return cachedDevTools;
+  }
 };

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly points out that repeatedly creating DevTools connections and performing version matching is inefficient and can lead to resource leaks. The proposed caching mechanism is an effective solution to ensure the connection is created only once.


[possible issue] Prevent multiple connection creation

✅ Prevent multiple connection creation

To prevent resource leaks from multiple connections, cache the BiDi instance so it is created only once. The current implementation creates a new connection every time getBiDi() is called.

java/src/org/openqa/selenium/bidi/BiDiProvider.java [47-56]

-return () -> {
-  URI wsUri = getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));
-
-  HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
-  ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
-  HttpClient wsClient = clientFactory.createClient(wsConfig);
-  Connection connection = new Connection(wsClient, wsUri.toString());
-
-  return Optional.of(new BiDi(connection));
+return new HasBiDi() {
+  private volatile Optional<BiDi> biDi;
+  
+  @Override
+  public Optional<BiDi> getBiDi() {
+    if (biDi == null) {
+      synchronized (this) {
+        if (biDi == null) {
+          URI wsUri = getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));
+          HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
+          ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
+          HttpClient wsClient = clientFactory.createClient(wsConfig);
+          Connection connection = new Connection(wsClient, wsUri.toString());
+          biDi = Optional.of(new BiDi(connection));
+        }
+      }
+    }
+    return biDi;
+  }
 };

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a significant issue where the PR's change to lazy-load the connection would create a new connection on every call, leading to resource leaks. The proposed fix using double-checked locking correctly implements a cached, lazy-initialized connection, which is a critical improvement for correctness and performance.


[general] Simplify lazy-loading with a memoized supplier

✅ Simplify lazy-loading with a memoized supplier

Replace the manual double-checked locking implementation with Guava's Suppliers.memoize to simplify the code and improve robustness for lazy initialization.

java/src/org/openqa/selenium/bidi/BiDiProvider.java [47-68]

 return new HasBiDi() {
-  private volatile Optional<BiDi> biDi;
+  private final Supplier<Optional<BiDi>> biDiSupplier =
+      Suppliers.memoize(
+          () -> {
+            URI wsUri =
+                getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));
+
+            HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
+            ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
+            HttpClient wsClient = clientFactory.createClient(wsConfig);
+            Connection connection = new Connection(wsClient, wsUri.toString());
+
+            return Optional.of(new BiDi(connection));
+          });
 
   @Override
   public Optional<BiDi> maybeGetBiDi() {
-    if (biDi == null) {
-      synchronized (this) {
-        if (biDi == null) {
-          URI wsUri = getBiDiUrl(caps).orElseThrow(() -> new BiDiException("BiDi not supported"));
-
-          HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
-          ClientConfig wsConfig = ClientConfig.defaultConfig().baseUri(wsUri);
-          HttpClient wsClient = clientFactory.createClient(wsConfig);
-          Connection connection = new Connection(wsClient, wsUri.toString());
-
-          biDi = Optional.of(new BiDi(connection));
-        }
-      }
-    }
-    return biDi;
+    return biDiSupplier.get();
   }
 };

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the manual double-checked locking can be replaced by a simpler and more robust library utility (Suppliers.memoize), significantly improving code readability and maintainability.


[learned best practice] Use initialized Optional with DCL

✅ Use initialized Optional with DCL

Initialize devTools to Optional.empty() and use a local variable in the double-checked locking to avoid null checks and publication races.

java/src/org/openqa/selenium/devtools/DevToolsProvider.java [45-64]

 return new HasDevTools() {
-  private volatile Optional<DevTools> devTools;
+  private volatile Optional<DevTools> devTools = Optional.empty();
 
   @Override
   public Optional<DevTools> maybeGetDevTools() {
-    if (devTools == null) {
+    Optional<DevTools> local = devTools;
+    if (local.isEmpty()) {
       synchronized (this) {
-        if (devTools == null) {
+        local = devTools;
+        if (local.isEmpty()) {
           Object cdpVersion = caps.getCapability("se:cdpVersion");
           String version =
             cdpVersion instanceof String ? (String) cdpVersion : caps.getBrowserVersion();
 
           CdpInfo info = new CdpVersionFinder().match(version).orElseGet(NoOpCdpInfo::new);
-          this.devTools = SeleniumCdpConnection.create(caps).map(conn -> new DevTools(info::getDomains, conn));
+          local = SeleniumCdpConnection.create(caps).map(conn -> new DevTools(info::getDomains, conn));
+          devTools = local;
         }
       }
     }
-    return devTools;
+    return local;
   }
 };

Suggestion importance[1-10]: 5

__

Why: Relevant best practice - Initialize attributes to sane defaults to avoid null/partially-initialized states, especially when using double-checked locking.



                     PR 16333 (2025-09-18)                    
[general] Restrict constant visibility for better encapsulation

✅ Restrict constant visibility for better encapsulation

Change the ChromiumIgnoreReason constant's visibility from public to private to improve encapsulation, as it is only used within the WebExtensionTest class.

dotnet/test/common/BiDi/WebExtension/WebExtensionTest.cs [30-36]

-public const string ChromiumIgnoreReason = """
+private const string ChromiumIgnoreReason = """
     The following test suite wants to set driver arguments via Options, but it breaks CDP/DevTools tests.
     The desired arguments (for Chromium only?):
     --enable-unsafe-extension-debugging
     --remote-debugging-pipe
     Ignoring these tests for now. Hopefully https://github.com/SeleniumHQ/selenium/issues/15536 will be resolved soon.
     """;

Suggestion importance[1-10]: 4

__

Why: The suggestion correctly identifies that the ChromiumIgnoreReason constant can be made private to improve encapsulation, as it is only used within its declaring class.



                     PR 16327 (2025-09-16)                    
[learned best practice] Add missing @Override annotation

✅ Add missing @Override annotation

Add the @Override annotation to getUpNodes() since it implements the new interface method, enabling compile-time checks.

java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java [366-376]

+@Override
 public Set<NodeStatus> getUpNodes() {
   Lock readLock = this.lock.readLock();
   readLock.lock();
   try {
     return model.getSnapshot().stream()
         .filter(node -> UP.equals(node.getAvailability()))
         .collect(ImmutableSet.toImmutableSet());
   } finally {
     readLock.unlock();
   }
 }

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Explicitly annotate interface implementations with @Override to ensure contract adherence and improve maintainability.



                     PR 16325 (2025-09-15)                    
[possible issue] Sort events to prevent flaky tests

✅ Sort events to prevent flaky tests

To prevent flaky tests, wait for both download events, sort them by URL to ensure a deterministic order, and then validate the properties of each event.

py/test/selenium/webdriver/common/bidi_browsing_context_tests.py [828-840]

-WebDriverWait(driver, 5).until(lambda d: len(events_received) > 1)
+WebDriverWait(driver, 5).until(lambda d: len(events_received) == 2)
 
 assert len(events_received) == 2
 
-download_event = events_received[0]
-assert download_event.download_params is not None
-assert download_event.download_params.status == "complete"
-assert download_event.download_params.context == context_id
-assert download_event.download_params.timestamp is not None
-assert "downloads/file_1.txt" in download_event.download_params.url
-# we assert that atleast the str "file_1" is present in the downloaded file since multiple downloads
+# Sort events to have a deterministic order for assertions
+events_received.sort(key=lambda e: e.download_params.url)
+
+download_event_1 = events_received[0]
+assert download_event_1.download_params is not None
+assert download_event_1.download_params.status == "complete"
+assert download_event_1.download_params.context == context_id
+assert download_event_1.download_params.timestamp is not None
+assert "downloads/file_1.txt" in download_event_1.download_params.url
+# we assert that at least the str "file_1" is present in the downloaded file since multiple downloads
 # will have numbered suffix like file_1 (1)
-assert "file_1" in download_event.download_params.filepath
+assert "file_1" in download_event_1.download_params.filepath
 
+download_event_2 = events_received[1]
+assert download_event_2.download_params is not None
+assert download_event_2.download_params.status == "complete"
+assert download_event_2.download_params.context == context_id
+assert download_event_2.download_params.timestamp is not None
+assert "downloads/file_2.txt" in download_event_2.download_params.url
+assert "file_2" in download_event_2.download_params.filepath
+

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a potential for flaky tests due to the non-deterministic order of asynchronous download events and provides a robust solution by sorting the events before assertion, which is a critical improvement for test reliability.



                     PR 16309 (2025-09-09)                    
[possible issue] Preserve None for optional fields

✅ Preserve None for optional fields

Avoid coercing optional values to strings, since str(None) becomes the literal "None", which will be sent to the remote as an invalid value. Pass these through unchanged so None remains None and only actual strings are forwarded.

py/selenium/webdriver/chromium/webdriver.py [63-69]

 executor = ChromiumRemoteConnection(
             remote_server_addr=self.service.service_url,
-            browser_name=str(browser_name),
-            vendor_prefix=str(vendor_prefix),
+            browser_name=browser_name,
+            vendor_prefix=vendor_prefix,
             keep_alive=keep_alive,
             ignore_proxy=options._ignore_local_proxy,
         )

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies that casting None to a string results in the literal "None", which is likely an invalid value for browser_name and vendor_prefix, thus fixing a bug introduced in the PR.



                     PR 16271 (2025-08-29)                    
[general] Properly stop driver before nullifying

✅ Properly stop driver before nullifying

Setting the global variable to None without properly stopping the driver can lead to resource leaks. The driver should be stopped before nullifying the reference to ensure proper cleanup.

py/conftest.py [344-345]

 if request.node.get_closest_marker("no_driver_after_test"):
+    if selenium_driver is not None:
+        selenium_driver.stop_driver()
     selenium_driver = None

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that nullifying selenium_driver without stopping it first is fragile and can lead to resource leaks, making the code more robust and consistent with other cleanup logic in the file.



                     PR 16262 (2025-08-26)                    
[possible issue] Fix broken version specifier

✅ Fix broken version specifier

The version specifier is invalid due to a missing comma between the lower and upper bounds. This will break dependency parsing and resolution. Add a comma to separate the specifiers.

py/BUILD.bazel [335]

-"websocket-client>=1.8.0<2.0",
+"websocket-client>=1.8.0,<2.0",

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a malformed version specifier for websocket-client that lacks a comma, which would break dependency resolution, making this a critical fix.


[possible issue] Correct malformed dependency range

✅ Correct malformed dependency range

The dependency constraint is malformed because it lacks a comma between specifiers, violating PEP 508. Insert the comma to ensure valid parsing by build tools.

py/pyproject.toml [34]

-"websocket-client>=1.8.0<2.0",
+"websocket-client>=1.8.0,<2.0",

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a malformed dependency constraint for websocket-client that violates PEP 508 and would break dependency installation, making this a critical fix.



                     PR 16250 (2025-08-24)                    
[general] Ensure attribute is always initialized

✅ Ensure attribute is always initialized

Initialize self.log_output with a default to avoid potential mypy "possibly uninitialized" errors when new branches are added. Also fix minor formatting for readability. This keeps the attribute always defined and improves maintainability.

py/selenium/webdriver/common/service.py [60-68]

-self.log_output: Optional[Union[int, IOBase]]
+self.log_output: Optional[Union[int, IOBase]] = None
 if isinstance(log_output, str):
     self.log_output = cast(IOBase, open(log_output, "a+", encoding="utf-8"))
 elif log_output == subprocess.STDOUT:
     self.log_output = None
 elif log_output is None or log_output == subprocess.DEVNULL:
     self.log_output = subprocess.DEVNULL
 else:
-    self.log_output = cast(Union[int, IOBase],log_output)
+    self.log_output = cast(Union[int, IOBase], log_output)

Suggestion importance[1-10]: 3

__

Why: The suggestion to initialize self.log_output at declaration is good practice for robustness and readability, although the existing if/elif/else structure already ensures it is always assigned a value.



                     PR 16248 (2025-08-23)                    
[learned best practice] Validate WebSocket constructor inputs

✅ Validate WebSocket constructor inputs

Validate the constructor parameters to ensure they are present and of the expected types/ranges. Default or raise clear errors for invalid values to prevent subtle runtime issues.

py/selenium/webdriver/remote/websocket_connection.py [31-39]

 class WebSocketConnection:
     _max_log_message_size = 9999
 
     def __init__(self, url, timeout, interval):
+        if not url:
+            raise ValueError("WebSocket URL must be provided")
+        if timeout is None or timeout <= 0:
+            raise ValueError("timeout must be a positive number")
+        if interval is None or interval <= 0:
+            raise ValueError("interval must be a positive number")
+
         self.callbacks = {}
         self.session_id = None
         self.url = url
-        self.response_wait_timeout = timeout
-        self.response_wait_interval = interval
+        self.response_wait_timeout = float(timeout)
+        self.response_wait_interval = float(interval)

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Add null checks and validation for parameters and variables before using them to prevent runtime errors.



                     PR 16233 (2025-08-21)                    
[learned best practice] Fix incorrect Javadoc reference

✅ Fix incorrect Javadoc reference

The Javadoc text references TargetLocator#alert(), which is unrelated to element screenshots, and misuses "@see" in a @param. Update the description to reflect WebElement.getScreenshotAs and fix parameter docs and punctuation.

java/src/org/openqa/selenium/support/events/WebDriverListener.java [613-622]

 /**
- * This action will be performed each time after {@link WebDriver.TargetLocator#alert()} is
- * called.
+ * This method will be called after {@link WebElement#getScreenshotAs(OutputType)} is called.
  *
- * @param element - decorated WebElement instance
- * @param target - target type, @see OutputType
- * @param result - object in which is stored information about the screenshot.
- * @param <X> - return type for getScreenshotAs.
+ * @param element the decorated WebElement instance
+ * @param target the target type, {@link OutputType}
+ * @param result the screenshot result
+ * @param <X> the return type for getScreenshotAs
  */
 default <X> void afterGetScreenshotAs(WebElement element, OutputType<X> target, X result) {}

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Fix syntax errors, typos, and naming inconsistencies in comments and documentation.


[learned best practice] Correct Javadoc parameter links

✅ Correct Javadoc parameter links

**

    • This action will be performed each time after {@link WebDriver.TargetLocator#alert()} is
    • called.
    • This method will be called after {@link TakesScreenshot#getScreenshotAs(OutputType)} is called.
    • @param element - decorated WebElement instance
    • @param target - target type, @see OutputType






**In Javadoc, avoid using "@see" within @param descriptions; link the type directly with {@link}. This improves clarity and tooling parsing. Update the parameter docs to use proper links and consistent punctuation.**

[java/src/org/openqa/selenium/support/events/WebDriverListener.java [335-343]](https://github.com/SeleniumHQ/selenium/pull/16233/files#diff-27fd3dec4abffb9b78d39c4f8ca918bcb5cd04926e3d252d2398f561f5ad61ffR335-R343)

```diff
 /**
- * This method will be called before {@link TakesScreenshot#getScreenshotAs(OutputType)} is
- * called.
+ * This method will be called before {@link TakesScreenshot#getScreenshotAs(OutputType)} is called.
  *
- * @param driver - decorated WebDriver instance
- * @param target - target type, @see OutputType
- * @param <X> - return type for getScreenshotAs.
+ * @param driver the decorated WebDriver instance
+ * @param target the target type, {@link OutputType}
+ * @param <X> the return type for getScreenshotAs
  */
 default <X> void beforeGetScreenshotAs(WebDriver driver, OutputType<X> target) {}

Suggestion importance[1-10]: 5

__

Why: Relevant best practice - Fix string formatting and interpolation issues; avoid ambiguous or incorrect Javadoc tags and references.


[learned best practice] Clean up Javadoc references

✅ Clean up Javadoc references

** * This method will be called after {@link TakesScreenshot#getScreenshotAs(OutputType)} is called. *

    • @param driver - decorated WebDriver instance
    • @param target - target type, @see OutputType
    • @param result - object in which is stored information about the screenshot.
    • @param - return type for getScreenshotAs.
    • @param driver decorated WebDriver instance
    • @param target target type, see {@link OutputType}
    • @param result object that stores the screenshot information
    • @param return type for getScreenshotAs */ default void afterGetScreenshotAs(WebDriver driver, OutputType target, X result) {}

@@ -607,19 +607,19 @@ * This method will be called before {@link TakesScreenshot#getScreenshotAs(OutputType)} is * called. *

    • @param element - decorated WebElement instance
    • @param target - target type, @see OutputType
    • @param - return type for getScreenshotAs.
    • @param element decorated WebElement instance
    • @param target target type, see {@link OutputType}
    • @param return type for getScreenshotAs */ default void beforeGetScreenshotAs(WebElement element, OutputType target) {}

/** * This method will be called after {@link TakesScreenshot#getScreenshotAs(OutputType)} is called. *

    • @param element - decorated WebElement instance
    • @param target - target type, @see OutputType
    • @param result - object in which is stored information about the screenshot.
    • @param - return type for getScreenshotAs.
    • @param element decorated WebElement instance
    • @param target target type, see {@link OutputType}
    • @param result result object that stores the screenshot information
    • @param return type for getScreenshotAs






**Replace "@see" inline in @param descriptions with proper Javadoc links or move them to @see tags. Also clarify generic return type wording and remove trailing periods in parameter descriptions for consistency.**

[java/src/org/openqa/selenium/support/events/WebDriverListener.java [335-624]](https://github.com/SeleniumHQ/selenium/pull/16233/files#diff-27fd3dec4abffb9b78d39c4f8ca918bcb5cd04926e3d252d2398f561f5ad61ffR335-R624)

```diff
 /**
- * This method will be called before {@link TakesScreenshot#getScreenshotAs(OutputType)} is
- * called.
+ * This method will be called before {@link TakesScreenshot#getScreenshotAs(OutputType)} is called.
  *
- * @param driver - decorated WebDriver instance
- * @param target - target type, @see OutputType
- * @param <X> - return type for getScreenshotAs.
+ * @param driver decorated WebDriver instance
+ * @param target target type, see {@link OutputType}
+ * @param <X> return type for getScreenshotAs
  */
 default <X> void beforeGetScreenshotAs(WebDriver driver, OutputType<X> target) {}
 
 /**
  * This method will be called after {@link TakesScreenshot#getScreenshotAs(OutputType)} is called.
  *
- * @param driver - decorated WebDriver instance
- * @param target - target type, @see OutputType
- * @param result - object in which is stored information about the screenshot.
- * @param <X> - return type for getScreenshotAs.
+ * @param driver decorated WebDriver instance
+ * @param target target type, see {@link OutputType}
+ * @param result object that stores the screenshot information
+ * @param <X> return type for getScreenshotAs
  */
 default <X> void afterGetScreenshotAs(WebDriver driver, OutputType<X> target, X result) {}
 
 /**
- * This method will be called before {@link TakesScreenshot#getScreenshotAs(OutputType)} is
- * called.
+ * This method will be called before {@link TakesScreenshot#getScreenshotAs(OutputType)} is called.
  *
- * @param element - decorated WebElement instance
- * @param target - target type, @see OutputType
- * @param <X> - return type for getScreenshotAs.
+ * @param element decorated WebElement instance
+ * @param target target type, see {@link OutputType}
+ * @param <X> return type for getScreenshotAs
  */
 default <X> void beforeGetScreenshotAs(WebElement element, OutputType<X> target) {}
 
 /**
  * This method will be called after {@link TakesScreenshot#getScreenshotAs(OutputType)} is called.
  *
- * @param element - decorated WebElement instance
- * @param target - target type, @see OutputType
- * @param result - object in which is stored information about the screenshot.
- * @param <X> - return type for getScreenshotAs.
+ * @param element decorated WebElement instance
+ * @param target target type, see {@link OutputType}
+ * @param result object that stores the screenshot information
+ * @param <X> return type for getScreenshotAs
  */
 default <X> void afterGetScreenshotAs(WebElement element, OutputType<X> target, X result) {}

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Fix Javadoc formatting and reference style to avoid ambiguity and maintain consistent documentation quality.


[general] Fix misleading parameter name

✅ Fix misleading parameter name

The parameter name driver for WebElement callbacks is misleading and contradicts the interface, risking confusion and incorrect usage. Rename it to element to align with WebDriverListener method signatures and improve test clarity.

java/test/org/openqa/selenium/support/events/EventFiringDecoratorTest.java [476-483]

-public <X> void beforeGetScreenshotAs(WebElement driver, OutputType<X> target) {
+public <X> void beforeGetScreenshotAs(WebElement element, OutputType<X> target) {
   acc.append("beforeGetScreenshotAs ").append(target).append("\n");
 }
 
 @Override
-public <X> void afterGetScreenshotAs(WebElement driver, OutputType<X> target, X result) {
+public <X> void afterGetScreenshotAs(WebElement element, OutputType<X> target, X result) {
   acc.append("afterGetScreenshotAs ").append(target).append(" ").append(result).append("\n");
 }

Suggestion importance[1-10]: 4

__

Why: The suggestion correctly identifies a misleading parameter name driver for a WebElement and suggests renaming it to element for consistency with the interface, improving code clarity.



                     PR 16228 (2025-08-21)                    
[possible issue] Avoid unintended copies with multi-RID

✅ Avoid unintended copies with multi-RID

Guard against custom or cross-targeting builds where RID is set via TargetFramework or RuntimeIdentifiers by also checking that '$(RuntimeIdentifiers)' is empty. This prevents duplicate or incorrect asset copying when NuGet already selects runtime-specific assets.

dotnet/src/webdriver/assets/nuget/build/netstandard2.0/Selenium.WebDriver.targets [4-9]

 <!-- Only run if the consumer did NOT set a RID (so NuGet won't select runtimes assets),
      and only for .NET Framework projects where this problem is common. -->
-<ItemGroup Condition="'$(RuntimeIdentifier)' == '' and '$(TargetFrameworkIdentifier)' == '.NETFramework'">
+<ItemGroup Condition="'$(RuntimeIdentifier)' == '' and '$(RuntimeIdentifiers)' == '' and '$(TargetFrameworkIdentifier)' == '.NETFramework'">
   <Content Include="$(MSBuildThisFileDirectory)..\..\runtimes\win\native\selenium-manager.exe">
     <Link>runtimes\win\native\%(Filename)%(Extension)</Link>
     <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that checking only $(RuntimeIdentifier) is insufficient, as $(RuntimeIdentifiers) can be used for multi-targeted builds, making the proposed condition more robust and aligned with the PR's intent.



                     PR 16212 (2025-08-19)                    
[learned best practice] Close HTTP response via context manager

✅ Close HTTP response via context manager

Ensure the HTTP response object is properly closed by using a context manager. This prevents potential resource leaks under heavy usage or exceptions.

py/selenium/webdriver/common/utils.py [150-151]

-res = urllib.request.urlopen(f"{scheme}://{host}:{port}/status")
-return res.getcode() == 200
+with urllib.request.urlopen(f"{scheme}://{host}:{port}/status") as res:
+    return res.getcode() == 200

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Use proper resource disposal patterns with context managers to prevent leaks when working with network/IO resources.



                     PR 16205 (2025-08-18)                    
[general] Mark removed API as obsolete error

✅ Mark removed API as obsolete error

Remove UnsubscribeByAttributesOptions since attribute-based unsubscribe is no longer supported, preventing accidental usage and API drift. If kept for binary compatibility, mark it [Obsolete(true)] to produce compile-time errors.

dotnet/src/webdriver/BiDi/Session/UnsubscribeCommand.cs [32-35]

+[Obsolete("Unsubscribe by attributes is no longer supported. Use unsubscribe by subscription id instead.", true)]
 public sealed class UnsubscribeByAttributesOptions : CommandOptions
 {
     public IEnumerable<BrowsingContext.BrowsingContext>? Contexts { get; set; }
 }

Suggestion importance[1-10]: 7

__

Why: The PR removes the functionality associated with UnsubscribeByAttributesOptions but leaves the class in the public API, so marking it as obsolete improves API consistency and prevents misuse.



                     PR 16191 (2025-08-16)                    
[possible issue] Fix malformed bug report link

✅ Fix malformed bug report link

Fix the malformed sentence and extra parenthesis to avoid confusing readers and broken rendering. Combine the text and link properly, using a single closing parenthesis and clear wording.

CONTRIBUTING.md [17-18]

-Issues shouldn't be used for support. To raise a bug, please go here-
-(https://github.com/SeleniumHQ/selenium/issues)).
+Issues shouldn't be used for support. To report a bug, please open an issue at: https://github.com/SeleniumHQ/selenium/issues

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies and fixes a malformed sentence and link, which has an extra parenthesis and unusual punctuation, improving the clarity and correctness of the documentation.


[general] Clarify Slack channel reference

✅ Clarify Slack channel reference

Clarify the Slack reference by using a direct, descriptive link label and avoid bare URLs in parentheses for better readability and accessibility. This reduces ambiguity and improves formatting consistency.

CONTRIBUTING.md [19-20]

-Discussion of high level project ideas or non-technical topics should
-move to the Selenium Slack channel (https://inviter.co/seleniumhq).
+Discussion of high level project ideas or non-technical topics should move to the Selenium Slack channel: https://inviter.co/seleniumhq

Suggestion importance[1-10]: 3

__

Why: The suggestion proposes a minor stylistic change to improve readability by replacing parentheses around a URL with a colon, which is a valid but low-impact improvement.



                     PR 16174 (2025-08-13)                    
[possible issue] Prevent invalid value reassignment

✅ Prevent invalid value reassignment

Avoid overwriting value with a non-dict message, which can lead to later .get() accesses failing. Only reassign value when message is a dict; otherwise keep value intact.

py/selenium/webdriver/remote/errorhandler.py [174-179]

 if not isinstance(message, str):
-    value = message
     if isinstance(message, dict):
+        value = message
         message = message.get("message")
     else:
         message = None

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that value should only be reassigned if message is a dictionary, preventing potential AttributeError exceptions on subsequent .get() calls on value.



                     PR 16169 (2025-08-13)                    
[general] Clarify label formatting

✅ Clarify label formatting

Use backticks around the label name in the link text to match GitHub’s UI and avoid ambiguity. This also helps distinguish the label from surrounding prose.

CONTRIBUTING.md [60]

-[issues labelled good first issue](https://github.com/SeleniumHQ/selenium/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22)
+[issues labelled `good first issue`](https://github.com/SeleniumHQ/selenium/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22)

Suggestion importance[1-10]: 3

__

Why: This is a minor stylistic suggestion that improves readability by using backticks for the label name, which is a good practice.



                     PR 16163 (2025-08-12)                    
[general] Bound status probe timeout

✅ Bound status probe timeout

Add a short read timeout specifically for the /status probe to avoid hanging on an unresponsive node and blocking session creation. Use a bounded timeout (e.g., a few seconds) independent from the main client timeout.

java/src/org/openqa/selenium/grid/router/HandleSession.java [258-293]

-try (HttpClient httpClient = httpClientFactory.createClient(config)) {
+try (HttpClient httpClient = httpClientFactory.createClient(
+    ClientConfig.defaultConfig()
+        .baseUri(uri)
+        .readTimeout(Duration.ofSeconds(5))
+        .withRetries())) {
   HttpRequest statusRequest = new HttpRequest(GET, "/status");
   HttpResponse res = httpClient.execute(statusRequest);
-  Reader reader = reader(res);
-  Json JSON = new Json();
-  JsonInput in = JSON.newInput(reader);
-  in.beginObject();
-  // Skip everything until we find "value"
-  while (in.hasNext()) {
-    if ("value".equals(in.nextName())) {
+  if (res.getStatus() == 200) {
+    try (Reader rdr = reader(res); JsonInput in = new Json().newInput(rdr)) {
       in.beginObject();
       while (in.hasNext()) {
-        if ("node".equals(in.nextName())) {
-          NodeStatus nodeStatus = in.read(NodeStatus.class);
-          sessionTimeout = nodeStatus.getSessionTimeout();
-          ...
+        String name = in.nextName();
+        if ("value".equals(name)) {
+          in.beginObject();
+          while (in.hasNext()) {
+            String inner = in.nextName();
+            if ("node".equals(inner)) {
+              NodeStatus nodeStatus = in.read(NodeStatus.class);
+              Duration parsed = nodeStatus.getSessionTimeout();
+              if (parsed != null && !parsed.isNegative() && !parsed.isZero()) {
+                sessionTimeout = parsed;
+              }
+            } else {
+              in.skipValue();
+            }
+          }
+          in.endObject();
         } else {
           in.skipValue();
         }
       }
-      in.endObject();
-    } else {
-      in.skipValue();
     }
   }
 } catch (Exception e) {
-  LOG.fine(
-      "Use default from ClientConfig (read timeout: "
-          + config.readTimeout().toSeconds()
-          + " seconds) for "
-          + uri);
+  LOG.fine("Use default from ClientConfig (read timeout: "
+      + config.readTimeout().toSeconds() + " seconds) for " + uri);
 }

Suggestion importance[1-10]: 8

__

Why: The suggestion addresses a potential performance issue where fetching the node status could hang indefinitely if the node is unresponsive, by proposing a separate, short timeout for this specific request, which is a critical improvement for stability.


[possible issue] Close parser and validate response

✅ Close parser and validate response

Ensure the JsonInput is always closed to avoid resource leaks. Additionally, verify the HTTP response status and content type before parsing to prevent parsing errors on non-200 responses or non-JSON bodies.

java/src/org/openqa/selenium/grid/router/HandleSession.java [255-296]

 private ClientConfig fetchNodeSessionTimeout(URI uri) {
   ClientConfig config = ClientConfig.defaultConfig().baseUri(uri).withRetries();
   Duration sessionTimeout = config.readTimeout();
   try (HttpClient httpClient = httpClientFactory.createClient(config)) {
     HttpRequest statusRequest = new HttpRequest(GET, "/status");
     HttpResponse res = httpClient.execute(statusRequest);
-    Reader reader = reader(res);
-    Json JSON = new Json();
-    JsonInput in = JSON.newInput(reader);
-    in.beginObject();
-    // Skip everything until we find "value"
-    while (in.hasNext()) {
-      if ("value".equals(in.nextName())) {
+    if (res.getStatus() == 200 && res.getHeader("Content-Type") != null && res.getHeader("Content-Type").contains("application/json")) {
+      try (Reader rdr = reader(res); JsonInput in = new Json().newInput(rdr)) {
         in.beginObject();
         while (in.hasNext()) {
-          if ("node".equals(in.nextName())) {
-            NodeStatus nodeStatus = in.read(NodeStatus.class);
-            sessionTimeout = nodeStatus.getSessionTimeout();
-            LOG.fine(
-                "Fetched session timeout from node status (read timeout: "
-                    + sessionTimeout.toSeconds()
-                    + " seconds) for "
-                    + uri);
+          String name = in.nextName();
+          if ("value".equals(name)) {
+            in.beginObject();
+            while (in.hasNext()) {
+              String inner = in.nextName();
+              if ("node".equals(inner)) {
+                NodeStatus nodeStatus = in.read(NodeStatus.class);
+                sessionTimeout = nodeStatus.getSessionTimeout();
+                LOG.fine("Fetched session timeout from node status (read timeout: "
+                    + sessionTimeout.toSeconds() + " seconds) for " + uri);
+              } else {
+                in.skipValue();
+              }
+            }
+            in.endObject();
           } else {
             in.skipValue();
           }
         }
-        in.endObject();
-      } else {
-        in.skipValue();
       }
+    } else {
+      LOG.fine("Non-OK or non-JSON status response from " + uri + " for /status, using default read timeout.");
     }
   } catch (Exception e) {
-    LOG.fine(
-        "Use default from ClientConfig (read timeout: "
-            + config.readTimeout().toSeconds()
-            + " seconds) for "
-            + uri);
+    LOG.fine("Use default from ClientConfig (read timeout: "
+        + config.readTimeout().toSeconds() + " seconds) for " + uri);
   }
-  config = config.readTimeout(sessionTimeout);
-  return config;
+  return config.readTimeout(sessionTimeout);
 }

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential resource leak by not closing the JsonInput and Reader, and improves robustness by adding a check for the HTTP response status and content type before parsing.



                     PR 16155 (2025-08-10)                    
[general] Use capacity check instead of slot scan

✅ Use capacity check instead of slot scan

Avoid iterating over all slots just to detect a free one by leveraging NodeStatus.hasCapacity() if it already encapsulates this logic. This reduces per-call overhead and keeps the intent consistent with other uses. Fall back to a short-circuiting check if hasCapacity() is insufficient.

java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java [509-527]

 protected Set<NodeStatus> getAvailableNodes() {
   Lock readLock = this.lock.readLock();
   readLock.lock();
   try {
     return model.getSnapshot().stream()
-        .filter(
-            node -> {
-              // Only consider UP nodes (not DOWN, DRAINING, etc.)
-              if (!UP.equals(node.getAvailability())) {
-                return false;
-              }
-              // Consider node has at least one free slot
-              return node.getSlots().stream().anyMatch(slot -> slot.getSession() == null);
-            })
+        .filter(node -> UP.equals(node.getAvailability()) && node.hasCapacity())
         .collect(toImmutableSet());
   } finally {
     readLock.unlock();
   }
 }

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly proposes using node.hasCapacity() to simplify the filter logic, which improves readability and aligns with its usage elsewhere in the PR.



                     PR 16153 (2025-08-09)                    
[possible issue] Scope version guard to Firefox

✅ Scope version guard to Firefox

The except guard mixes a version exclusion with browser exclusions but omits the browser key for the version condition. Add the browser: :firefox key to ensure the version comparison only applies to Firefox and doesn't accidentally affect other browsers.

rb/spec/integration/selenium/webdriver/manager_spec.rb [172-174]

-except: [{version: GlobalTestEnv.beta_browser_version(:firefox),
+except: [{browser: :firefox, version: GlobalTestEnv.beta_browser_version(:firefox),
            reason: 'https://github.com/mozilla/geckodriver/issues/1842'},
          {browser: %i[safari safari_preview]}]

Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a bug introduced in the PR where removing the browser: :firefox key would cause the version exception to apply to all browsers, not just Firefox.



                     PR 16151 (2025-08-09)                    
[incremental [*]] Process all health check batches

✅ Process all health check batches

Ensure all batches are processed by removing the limit(maxConcurrentBatches) which drops excess batches, or implement a proper throttling mechanism that still visits every batch

java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java [402-422]

 private void processBatchesInParallel(List<List<Runnable>> batches, int maxConcurrentBatches) {
   if (batches.isEmpty()) {
     return;
   }
 
-  // Process batches with controlled parallelism
   batches.parallelStream()
-      .limit(maxConcurrentBatches)
       .forEach(
           batch ->
               batch.parallelStream()
                   .forEach(
                       r -> {
                         try {
                           r.run();
                         } catch (Throwable t) {
-                          LOG.log(
-                              getDebugLogLevel(), "Health check execution failed in batch", t);
+                          LOG.log(getDebugLogLevel(), "Health check execution failed in batch", t);
                         }
                       }));
 }

Suggestion importance[1-10]: 9

__

Why: This suggestion identifies a critical bug where limit(maxConcurrentBatches) would cause health checks for some nodes to be skipped entirely, and the proposed fix correctly ensures all batches are processed.


[learned best practice] Simplify heartbeat timeout math

✅ Simplify heartbeat timeout math

Use consistent and clearer time calculations; avoid chained multiply/divide on Duration which can be error-prone. Replace with a single multiplication by half the multiplier to match intent and readability.

java/src/org/openqa/selenium/grid/distributor/local/LocalGridModel.java [245-247]

 Instant lostTime =
-    lastTouched.plus(
-        node.getHeartbeatPeriod().multipliedBy(PURGE_TIMEOUT_MULTIPLIER).dividedBy(2));
+    lastTouched.plus(node.getHeartbeatPeriod().multipliedBy(PURGE_TIMEOUT_MULTIPLIER / 2));

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Fix syntax errors, typos, and naming inconsistencies to maintain code quality.


[incremental [*]] Cancel tasks and free resources

✅ Cancel tasks and free resources

Ensure scheduled tasks are cancelled and resources are released on close; cancel per-node futures and clear maps to prevent thread and memory leaks

java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java [535-539]

 @Override
 public void close() {
   LOG.info("Shutting down LocalNodeRegistry");
+  Lock writeLock = lock.writeLock();
+  writeLock.lock();
+  try {
+    scheduledHealthChecks.values().forEach(f -> {
+      if (f != null) {
+        f.cancel(false);
+      }
+    });
+    scheduledHealthChecks.clear();
+    allChecks.clear();
+    nodes.values().forEach(n -> {
+      if (n instanceof RemoteNode) {
+        try {
+          ((RemoteNode) n).close();
+        } catch (Exception e) {
+          LOG.log(Level.WARNING, "Unable to close node properly: " + e.getMessage());
+        }
+      }
+    });
+    nodes.clear();
+  } finally {
+    writeLock.unlock();
+  }
 }

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly points out a critical resource leak where scheduled health checks are not cancelled on close, which would prevent proper shutdown and cause memory leaks.


[possible issue] Use concurrent maps for health checks

✅ Use concurrent maps for health checks

Access to allChecks and scheduledHealthChecks occurs under locks but the maps themselves are non-concurrent. Replace them with concurrent maps to avoid accidental unsynchronized access paths and reduce risk of race conditions during listener callbacks. This is critical since listeners and scheduled tasks can run concurrently.

java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java [85-86]

-private final Map<NodeId, Runnable> allChecks = new HashMap<>();
-private final Map<NodeId, ScheduledFuture<?>> scheduledHealthChecks = new HashMap<>();
+private final Map<NodeId, Runnable> allChecks = new ConcurrentHashMap<>();
+private final Map<NodeId, ScheduledFuture<?>> scheduledHealthChecks = new ConcurrentHashMap<>();

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that using ConcurrentHashMap for allChecks and scheduledHealthChecks improves robustness and maintainability in a highly concurrent class, reducing the risk of future errors.


[possible issue] Fix integer division in timing

✅ Fix integer division in timing

The division PURGE_TIMEOUT_MULTIPLIER / 2 performs integer division; for odd values it truncates and may degrade timing logic. Compute using long arithmetic to preserve intended scaling, e.g., multiply first or use exact constants.

java/src/org/openqa/selenium/grid/distributor/local/LocalGridModel.java [245-248]

 Instant lostTime =
-    lastTouched.plus(node.getHeartbeatPeriod().multipliedBy(PURGE_TIMEOUT_MULTIPLIER / 2));
+    lastTouched.plus(node.getHeartbeatPeriod().multipliedBy((long) PURGE_TIMEOUT_MULTIPLIER).dividedBy(2));
 Instant deadTime =
-    lastTouched.plus(node.getHeartbeatPeriod().multipliedBy(PURGE_TIMEOUT_MULTIPLIER));
+    lastTouched.plus(node.getHeartbeatPeriod().multipliedBy((long) PURGE_TIMEOUT_MULTIPLIER));

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out that integer division on PURGE_TIMEOUT_MULTIPLIER could lead to precision loss, and using long arithmetic or floating-point division would make the timing calculation more robust.


[learned best practice] Cancel tasks and close nodes

✅ Cancel tasks and close nodes

Ensure scheduled tasks are cancelled and nodes are closed on shutdown. Cancel futures in scheduledHealthChecks, clear listeners if needed, and close any RemoteNode instances to prevent resource leaks.

java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java [532-535]

 @Override
 public void close() {
   LOG.info("Shutting down LocalNodeRegistry");
+  Lock writeLock = lock.writeLock();
+  writeLock.lock();
+  try {
+    // Cancel scheduled health checks
+    for (ScheduledFuture<?> future : scheduledHealthChecks.values()) {
+      if (future != null) {
+        future.cancel(false);
+      }
+    }
+    scheduledHealthChecks.clear();
+    allChecks.clear();
+    // Close remote nodes
+    for (Node node : nodes.values()) {
+      if (node instanceof RemoteNode) {
+        try {
+          ((RemoteNode) node).close();
+        } catch (Exception e) {
+          LOG.log(Level.WARNING, "Unable to close node properly: " + e.getMessage());
+        }
+      }
+    }
+    nodes.clear();
+  } finally {
+    writeLock.unlock();
+  }
 }

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Properly dispose and clean up scheduled tasks and resources to prevent leaks.


[learned best practice] Add parameter null validation

✅ Add parameter null validation

Validate inputs before using them. Add null checks for nodeUri, id, and availability to avoid potential NullPointerExceptions during logging and model updates.

java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java [292-304]

 public void updateNodeAvailability(URI nodeUri, NodeId id, Availability availability) {
+  Require.nonNull("Node URI", nodeUri);
+  Require.nonNull("Node ID", id);
+  Require.nonNull("Availability", availability);
   Lock writeLock = lock.writeLock();
   writeLock.lock();
   try {
     LOG.log(
         getDebugLogLevel(),
         String.format("Health check result for %s was %s", nodeUri, availability));
     model.setAvailability(id, availability);
     model.updateHealthCheckCount(id, availability);
   } finally {
     writeLock.unlock();
   }
 }

Suggestion importance[1-10]: 5

__

Why: Relevant best practice - Add null checks and validation for parameters before use to prevent NullPointerException.



                     PR 16136 (2025-08-06)                    
[possible issue] Fix version constraint separator syntax

✅ Fix version constraint separator syntax

The version constraint uses a semicolon separator which is not standard for pip dependency specifications. Use comma separator instead for better compatibility across packaging tools.

py/BUILD.bazel [330]

-"urllib3[socks]>=2.5.0;<3.0",
+"urllib3[socks]>=2.5.0,<3.0",

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies an invalid separator in the urllib3 version specifier; using a semicolon instead of a comma would cause dependency resolution to fail.


[general] Use explicit version constraint format

✅ Use explicit version constraint format

The version constraint should use <3.0 instead of <3 for better clarity and consistency with semantic versioning practices. This makes the upper bound more explicit and follows common Python packaging conventions.

py/BUILD.bazel [330]

-"urllib3[socks]>=2.5.0;<3",
+"urllib3[socks]>=2.5.0,<3.0",

Suggestion importance[1-10]: 8

__

Why: The suggestion corrects an invalid dependency specifier by changing the semicolon to a comma, which is crucial for correctness as per PEP 508.



                     PR 16131 (2025-08-05)                    
[general] Fix confusing property logic

✅ Fix confusing property logic

The property name LogTruncate is misleading since setting it to true actually disables truncation. Consider renaming to LogNoTruncate or inverting the logic to make the intent clearer.

dotnet/src/webdriver/Firefox/FirefoxDriverService.cs [202-205]

-if (this.LogTruncate)
+if (!this.LogTruncate)
 {
     argsBuilder.Append(" --log-no-truncate");
 }

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that the property name LogTruncate is confusing, as setting it to true disables truncation. This change would improve code clarity and maintainability.



                     PR 16114 (2025-07-31)                    
[possible issue] Fix missing comma in array

✅ Fix missing comma in array

Add a missing comma after "LICENSE" to properly separate the list items. Without the comma, this creates a syntax error in the TOML array.

py/pyproject.toml [71-72]

-"LICENSE"
+"LICENSE",
 "NOTICE"

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a missing comma in the TOML array, which is a syntax error that would cause the package build to fail.



                     PR 16112 (2025-07-31)                    
[general] Simplify dictionary building logic

✅ Simplify dictionary building logic

The method manually checks each attribute and builds the dictionary, which is repetitive and error-prone. Use a mapping approach to dynamically build the result dictionary from instance attributes.

py/selenium/webdriver/common/bidi/session.py [80-100]

 def to_dict(self) -> Dict[str, str]:
     """Convert the UserPromptHandler to a dictionary for BiDi protocol.
 
     Returns:
     -------
         Dict[str, str]: Dictionary representation suitable for BiDi protocol
     """
+    field_mapping = {
+        'alert': 'alert',
+        'before_unload': 'beforeUnload',
+        'confirm': 'confirm',
+        'default': 'default',
+        'file': 'file',
+        'prompt': 'prompt',
+    }
+    
     result = {}
-    if self.alert is not None:
-        result["alert"] = self.alert
-    if self.before_unload is not None:
-        result["beforeUnload"] = self.before_unload
-    if self.confirm is not None:
-        result["confirm"] = self.confirm
-    if self.default is not None:
-        result["default"] = self.default
-    if self.file is not None:
-        result["file"] = self.file
-    if self.prompt is not None:
-        result["prompt"] = self.prompt
+    for attr_name, dict_key in field_mapping.items():
+        value = getattr(self, attr_name)
+        if value is not None:
+            result[dict_key] = value
     return result

Suggestion importance[1-10]: 5

__

Why: This suggestion improves maintainability by replacing a series of if statements with a more scalable, loop-based approach, making it easier to add new fields in the future.



                     PR 16098 (2025-07-28)                    
[general] Add enum validation before string conversion

✅ Add enum validation before string conversion

Add validation to ensure the enum value is defined before converting to string. This prevents potential issues if an invalid enum value is somehow assigned to LogLevel.

dotnet/src/webdriver/Chromium/ChromiumDriverService.cs [162-165]

 if (this.LogLevel != ChromiumDriverLogLevel.Default)
 {
-    argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " --log-level={0}", this.LogLevel.ToString().ToUpperInvariant()));
+    if (Enum.IsDefined(typeof(ChromiumDriverLogLevel), this.LogLevel))
+    {
+        argsBuilder.Append($" --log-level={this.LogLevel.ToString().ToUpperInvariant()}");
+    }
 }

Suggestion importance[1-10]: 7

__

Why: This is a good defensive programming suggestion that improves robustness by validating the enum value, preventing potential errors if an undefined value is assigned to the public LogLevel property.


[general] Use string interpolation for cleaner code

✅ Use string interpolation for cleaner code

Consider using string interpolation instead of string.Format for better readability and performance. The current approach is unnecessarily verbose for a simple string concatenation.

dotnet/src/webdriver/Chromium/ChromiumDriverService.cs [164]

-argsBuilder.Append(string.Format(CultureInfo.InvariantCulture, " --log-level={0}", this.LogLevel.ToString().ToUpperInvariant()));
+argsBuilder.Append($" --log-level={this.LogLevel.ToString().ToUpperInvariant()}");

Suggestion importance[1-10]: 4

__

Why: The suggestion to use string interpolation instead of string.Format is a valid style improvement that enhances code readability without changing functionality.



                     PR 16097 (2025-07-28)                    
[possible issue] Fix conditional redirection logic

✅ Fix conditional redirection logic

The redirection should be set unconditionally when LogPath is specified, not just when LogToConsole is true. This ensures proper log file functionality regardless of the LogToConsole state.

dotnet/src/webdriver/Firefox/FirefoxDriverService.cs [216-222]

 // LogToConsole and LogPath are mutually exclusive. LogPath takes precedence.
-if (this.LogToConsole)
-{
-    this.LogToConsole = false;
-    eventArgs.DriverServiceProcessStartInfo.RedirectStandardOutput = true;
-    eventArgs.DriverServiceProcessStartInfo.RedirectStandardError = true;
-}
+this.LogToConsole = false;
+eventArgs.DriverServiceProcessStartInfo.RedirectStandardOutput = true;
+eventArgs.DriverServiceProcessStartInfo.RedirectStandardError = true;

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a bug where setting the LogPath will not work as expected unless LogToConsole is also true, which is a significant flaw in the new logic.



                     PR 16081 (2025-07-23)                    
[possible issue] Add exception handling for async operations

✅ Add exception handling for async operations

The async method lacks exception handling which could cause unhandled exceptions to crash the application. Add try-catch blocks to handle potential I/O exceptions during stream reading and log writing operations.

dotnet/src/webdriver/Firefox/FirefoxDriverService.cs [346-356]

 private async Task ReadStreamAsync(StreamReader reader)
 {
-    string? line;
-    while ((line = await reader.ReadLineAsync()) != null)
+    try
     {
-        if (logWriter != null)
+        string? line;
+        while ((line = await reader.ReadLineAsync()) != null)
         {
-            logWriter.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {line}");
+            if (logWriter != null)
+            {
+                logWriter.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {line}");
+            }
         }
+    }
+    catch (Exception ex)
+    {
+        // Log or handle the exception appropriately
+        System.Diagnostics.Debug.WriteLine($"Error reading stream: {ex.Message}");
     }
 }

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the ReadStreamAsync method, being run as a fire-and-forget task, lacks exception handling, which could lead to unobserved exceptions and application instability.


[general] Set disposed field to null

✅ Set disposed field to null

The disposal logic should set the logWriter field to null after disposing to prevent potential use-after-dispose scenarios. This ensures the field cannot be accessed after disposal.

dotnet/src/webdriver/Firefox/FirefoxDriverService.cs [254-262]

 protected override void Dispose(bool disposing)
 {
     if (logWriter != null && disposing)
     {
         logWriter.Dispose();
+        logWriter = null;
     }
 
     base.Dispose(disposing);
 }

Suggestion importance[1-10]: 5

__

Why: Setting the logWriter field to null after disposal is a good practice that improves code robustness by preventing potential use-after-dispose errors.



                     PR 16070 (2025-07-18)                    
[possible issue] Add missing coordinates/error validation

✅ Add missing coordinates/error validation

Add validation to ensure at least one of coordinates or error is specified. Currently, both can be null which would result in an invalid geolocation override request.

java/src/org/openqa/selenium/bidi/emulation/SetGeolocationOverrideParameters.java [41-50]

 if (this.coordinates != null && this.error != null) {
   throw new IllegalArgumentException("Cannot specify both coordinates and error");
+}
+if (this.coordinates == null && this.error == null) {
+  throw new IllegalArgumentException("Must specify either coordinates or error");
 }
 if (this.contexts != null && this.userContexts != null) {
   throw new IllegalArgumentException("Cannot specify both contexts and userContexts");
 }
 
 if (this.contexts == null && this.userContexts == null) {
   throw new IllegalArgumentException("Must specify either contexts or userContexts");
 }

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a missing validation check that would prevent creating an invalid state where both coordinates and error are null, improving the robustness of the class.



                     PR 16061 (2025-07-16)                    
[possible issue] Handle potential None from regex

✅ Handle potential None from regex

The regex search could return None if the pattern doesn't match, causing an AttributeError when accessing index [1]. Add a check to handle cases where the URL format doesn't match the expected pattern.

scripts/update_multitool_binaries.py [40-42]

-version = re.search(f"download/(.*?)/{tool}", data[tool]["binaries"][0]["url"])[
-    1
-]
+version_match = re.search(f"download/(.*?)/{tool}", data[tool]["binaries"][0]["url"])
+if not version_match:
+    continue
+version = version_match[1]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies that re.search can return None, which would cause a TypeError, and provides a robust fix to prevent the script from crashing on unexpected URL formats.


[general] Use tag_name for version

✅ Use tag_name for version

Using .title assumes the release title contains the version, but GitHub releases often use .tag_name for version information. Consider using .tag_name instead for more reliable version extraction.

scripts/update_multitool_binaries.py [50-53]

 try:
-    new_version = Github().get_repo(user_repo).get_releases()[0].title
+    new_version = Github().get_repo(user_repo).get_releases()[0].tag_name
 except Exception:
     continue

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that a release tag_name is generally a more reliable source for a version string than its title, improving the script's robustness for different GitHub repositories.



                     PR 16048 (2025-07-13)                    
[general] Use platform-appropriate path separator

✅ Use platform-appropriate path separator

The code assumes semicolon as the path separator, but this may not be correct on all platforms. On Unix-like systems, the path separator is typically a colon (:). Consider using Path.PathSeparator or detecting the platform-appropriate separator.

dotnet/src/webdriver/SeleniumManager.cs [105-111]

 // Supporting .NET5+ applications deployed as self-contained applications (single file or AOT)
 var nativeDllSearchDirectories = AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES")?.ToString();
 
 if (nativeDllSearchDirectories is not null)
 {
-    probingPaths.AddRange(nativeDllSearchDirectories.Split(';').Select(path => Path.Combine(path, seleniumManagerFileName)));
+    probingPaths.AddRange(nativeDllSearchDirectories.Split(Path.PathSeparator).Select(path => Path.Combine(path, seleniumManagerFileName)));
 }

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a cross-platform bug where a hardcoded Windows path separator (;) is used, and the proposed fix using Path.PathSeparator is essential for correctness on non-Windows systems.


[general] Use LINQ FirstOrDefault for consistency

✅ Use LINQ FirstOrDefault for consistency

The Find method returns null when no matching element is found, but the subsequent null check uses is null pattern. Consider using FirstOrDefault with LINQ for consistency with the added using System.Linq import, or keep the current approach but ensure consistent null checking patterns throughout the codebase.

dotnet/src/webdriver/SeleniumManager.cs [134-140]

-binaryFullPath = probingPaths.Find(path => File.Exists(path));
+binaryFullPath = probingPaths.FirstOrDefault(File.Exists);
 
 if (binaryFullPath is null)
 {
     throw new FileNotFoundException(
         $"Unable to locate Selenium Manager binary in the following paths: {string.Join(", ", probingPaths)}");
 }

Suggestion importance[1-10]: 3

__

Why: The suggestion correctly proposes using FirstOrDefault for consistency with the newly added System.Linq import, but this is a minor stylistic improvement with no functional impact.


[learned best practice] Add null validation for directory

✅ Add null validation for directory

The currentDirectory variable should be validated for null before being used in Path.Combine() calls. Add a null check to prevent potential NullReferenceException if AppContext.BaseDirectory returns null.

dotnet/src/webdriver/SeleniumManager.cs [82-89]

+ArgumentNullException.ThrowIfNull(currentDirectory);
+
 binaryFullPath = platform switch
 {
     SupportedPlatform.Windows => Path.Combine(currentDirectory, "runtimes", "win", "native", "selenium-manager.exe"),
     SupportedPlatform.Linux => Path.Combine(currentDirectory, "runtimes", "linux", "native", "selenium-manager"),
     SupportedPlatform.MacOS => Path.Combine(currentDirectory, "runtimes", "osx", "native", "selenium-manager"),
     _ => throw new PlatformNotSupportedException(
         $"Selenium Manager doesn't support your runtime platform: {RuntimeInformation.OSDescription}"),
 };

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Add null checks and validation for parameters, properties, and return values before using them to prevent NullReferenceException and other runtime errors



                     PR 16032 (2025-07-09)                    
[general] Fix incorrect word usage

✅ Fix incorrect word usage

The word "preview" should be "purview" in this context. "Purview" means the scope or range of authority, while "preview" means a preliminary view or showing.

dotnet/src/webdriver/PinnedScript.cs [36-40]

 /// <remarks>
 /// This constructor is explicitly internal. Creation of pinned script objects
-/// is strictly the preview of Selenium, and should not be required by external
+/// is strictly the purview of Selenium, and should not be required by external
 /// libraries.
 /// </remarks>

Suggestion importance[1-10]: 3

__

Why: The suggestion correctly identifies that the word preview is used incorrectly in the comment, and the correct word should be purview.



                     PR 16022 (2025-07-08)                    
[general] Fix typo in JavaDoc comment

✅ Fix typo in JavaDoc comment

Fix the typo "br" to "be" in the JavaDoc comment. The word "br" should be "be" to make the sentence grammatically correct.

java/src/org/openqa/selenium/json/JsonOutput.java [267]

-* Specify whether the serialized JSON object should br formatted with line breaks and indentation
+* Specify whether the serialized JSON object should be formatted with line breaks and indentation

Suggestion importance[1-10]: 4

__

Why: The suggestion correctly identifies a typo ('br' should be 'be') in the Javadoc comment on a line modified by the PR, which improves documentation clarity.



                     PR 16016 (2025-07-07)                    
[general] Add proper resource disposal handling

Add proper resource disposal handling

The IPv4 fallback listener is not properly disposed if an exception occurs during its operation. Wrap the fallback listener in a using statement or try-finally block to ensure proper resource cleanup.

dotnet/src/webdriver/Internal/PortUtilities.cs [45-53]

 catch (SocketException)
 {
     // If IPv6Any is not supported, fallback to IPv4
     var listener = new TcpListener(IPAddress.Any, 0);
-    listener.Start();
-    int port = ((IPEndPoint)listener.LocalEndpoint).Port;
-    listener.Stop();
-    return port;
+    try
+    {
+        listener.Start();
+        int port = ((IPEndPoint)listener.LocalEndpoint).Port;
+        return port;
+    }
+    finally
+    {
+        listener.Stop();
+    }
 }

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential resource leak in the catch block, as an exception after new TcpListener would prevent listener.Stop() from being called.


[learned best practice] Use proper resource disposal patterns

Use proper resource disposal patterns

The TcpListener resources are not properly disposed in case of exceptions. Use using statements or try-finally blocks to ensure proper cleanup. This prevents resource leaks if exceptions occur between Start() and Stop() calls.

dotnet/src/webdriver/Internal/PortUtilities.cs [38-43]

-var listener = new TcpListener(IPAddress.IPv6Any, 0);
+using var listener = new TcpListener(IPAddress.IPv6Any, 0);
 listener.Server.DualMode = true; // Listen on both IPv4 and IPv6
 listener.Start();
 int port = ((IPEndPoint)listener.LocalEndpoint).Port;
-listener.Stop();
 return port;

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - 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.


[general] Preserve original socket exception details

✅ Preserve original socket exception details

The exception handling is too broad and may mask legitimate socket binding failures. Consider allowing the method to retry with different approaches or return a more specific error that indicates the system state rather than throwing immediately.

dotnet/src/webdriver/Internal/PortUtilities.cs [45-48]

 catch (SocketException ex)
 {
-    throw new InvalidOperationException("Unable to find a free port.", ex);
+    // Let the original SocketException bubble up as it provides more specific information
+    // about why port binding failed (e.g., no available ports, permission issues)
+    throw;
 }

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that wrapping SocketException in InvalidOperationException obscures the original, more specific error, making debugging harder.



                     PR 16003 (2025-07-06)                    
[incremental [*]] Handle IPv6 binding failures gracefully

✅ Handle IPv6 binding failures gracefully

Add a try-except block around the IPv6 socket creation and binding to handle cases where IPv6 is also unavailable, preventing potential unhandled exceptions that could crash the function.

py/selenium/webdriver/common/utils.py [34-44]

 free_socket = None
 try:
     # IPv4
     free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     free_socket.bind(("127.0.0.1", 0))
 except OSError:
     if free_socket:
         free_socket.close()
-    # IPv6
-    free_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
-    free_socket.bind(("::1", 0))
+    try:
+        # IPv6
+        free_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+        free_socket.bind(("::1", 0))
+    except OSError:
+        raise RuntimeError("Unable to bind to both IPv4 and IPv6")

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that if both IPv4 and IPv6 binding fail, an unhandled OSError would occur, and proposes a robust solution to catch this.


[learned best practice] Ensure proper socket cleanup

✅ Ensure proper socket cleanup

The socket resource is not properly cleaned up if an exception occurs after the IPv6 socket creation or during listen/getsockname operations. Use a try-finally block to ensure the socket is always closed, preventing resource leaks.

py/selenium/webdriver/common/utils.py [45-47]

 free_socket = None
 try:
     # IPv4
     free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     free_socket.bind(("127.0.0.1", 0))
 except OSError:
     if free_socket:
         free_socket.close()
     # IPv6
     free_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
     free_socket.bind(("::1", 0))
-free_socket.listen(5)
-port: int = free_socket.getsockname()[1]
-free_socket.close()
 
+try:
+    free_socket.listen(5)
+    port: int = free_socket.getsockname()[1]
+finally:
+    free_socket.close()
+

Suggestion importance[1-10]: 6

__

Why: Relevant best practice - 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.


[possible issue] Prevent socket resource leak

✅ Prevent socket resource leak

The IPv4 socket should be closed if binding fails to prevent resource leaks. Currently, if IPv4 socket creation succeeds but binding fails, the socket remains open when the exception is caught.

py/selenium/webdriver/common/utils.py [34-44]

+free_socket = None
 try:
     # IPv4
     free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     free_socket.bind(("127.0.0.1", 0))
 except OSError:
+    if free_socket:
+        free_socket.close()
     # IPv6
     free_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
     free_socket.bind(("::1", 0))
 free_socket.listen(5)
 port: int = free_socket.getsockname()[1]
 free_socket.close()

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a resource leak where the IPv4 socket is not closed if bind() fails, which is a valid correctness issue introduced in the PR.



                     PR 15978 (2025-06-29)                    
[general] Convert field to property

✅ Convert field to property

The Nodes field should be a property with a getter to maintain consistency with other similar classes in the codebase. This ensures proper encapsulation and follows C# conventions.

dotnet/src/webdriver/BiDi/BrowsingContext/LocateNodesCommand.cs [47]

-public readonly IReadOnlyList<Script.NodeRemoteValue> Nodes;
+public IReadOnlyList<Script.NodeRemoteValue> Nodes { get; }

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly points out that Nodes is a public field, while other similar classes in the PR use properties, and changing it improves consistency.



                     PR 15959 (2025-06-25)                    
[possible issue] Fix incorrect macOS key mapping

✅ 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 importance[1-10]: 8

__

Why: The suggestion correctly identifies that the macOS Options key is equivalent to the Alt key and should be mapped to right_alt (\ue052), not right_shift (\ue050). This is a valid and important correction to the key mapping logic.



                     PR 15948 (2025-06-24)                    
[possible issue] Fix duplicate Unicode key mappings

✅ 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 importance[1-10]: 9

__

Why: The suggestion correctly identifies that the Unicode values for OPTIONS (\uE050) and FUNCTION (\uE051) conflict with the newly added RIGHT_SHIFT (\ue050) and RIGHT_CONTROL (\ue051). This duplication could cause unexpected behavior and is a critical issue to fix. The proposed change resolves this conflict.



                     PR 15942 (2025-06-23)                    
[general] Add fallback for None reason

✅ 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 importance[1-10]: 7

__

Why: The suggestion correctly identifies that response.reason could be None and provides a sensible fallback to using the statuscode, similar to the original behavior. This improves the robustness of the error handling.



                     PR 15924 (2025-06-20)                    
[possible issue] Add missing null check validation

✅ 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 importance[1-10]: 8

__

Why: The suggestion correctly identifies that while the clientConfig parameter was annotated as @Nullable in the PR, the corresponding null-handling logic was missed. Adding this check prevents a potential NullPointerException and makes the constructor's behavior consistent with the other nullable parameters.



                     PR 15923 (2025-06-20)                    
[general] Reduce code duplication in paths

✅ Reduce code duplication in paths

Consider storing the base path in a variable to avoid code duplication and make the path easier to maintain. This reduces the risk of typos and makes future path changes simpler.

dotnet/test/common/Environment/TestWebServer.cs [56-63]

+var basePath = @"_main/java/test/org/openqa/selenium/environment/appserver";
 if (OperatingSystem.IsWindows())
 {
-    standaloneAppserverPath = runfiles.Rlocation(@"_main/java/test/org/openqa/selenium/environment/appserver.exe");
+    standaloneAppserverPath = runfiles.Rlocation(basePath + ".exe");
 }
 else
 {
-    standaloneAppserverPath = runfiles.Rlocation(@"_main/java/test/org/openqa/selenium/environment/appserver");
+    standaloneAppserverPath = runfiles.Rlocation(basePath);
 }

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies a duplicated string literal for the application path and proposes a valid refactoring. Extracting the common base path into a variable improves code readability and maintainability, which is a good practice.



                     PR 15916 (2025-06-19)                    
[possible issue] Fix incorrect event subscription name

✅ 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 importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical bug. The OnHistoryUpdatedAsync methods subscribe to the wrong event name, "browsingContext.fragmentNavigated", which would cause the new feature to not work as intended. The proposed fix to use "browsingContext.historyUpdated" is correct and essential for the functionality.



                     PR 15910 (2025-06-18)                    
[general] Verify Unicode key code standards

✅ 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

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the new Unicode values \uE050 and \uE051 are in the Private Use Area. This raises a valid concern about standardization and potential inconsistencies across different WebDriver implementations, which is a crucial consideration for a library like Selenium.



                     PR 15900 (2025-06-15)                    
[possible issue] Fix incomplete variable assignment

✅ Fix incomplete variable assignment

The else branch assigns None to nothing, which is a no-op. This should assign None to remote_server_addr or handle the case where command_executor doesn't have the expected attributes.

py/selenium/webdriver/remote/webdriver.py [126-129]

 if hasattr(command_executor, "client_config") and command_executor.client_config:
     remote_server_addr = command_executor.client_config.remote_server_addr
 else:
-    None
+    remote_server_addr = command_executor

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical bug. The else: None statement is a no-op, which would cause a NameError for remote_server_addr on line 132 if the if condition is false. The proposed fix correctly restores the original behavior for the else case.



                     PR 15889 (2025-06-11)                    
[possible issue] Fix string validation logic

✅ 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 importance[1-10]: 7

__

Why: The suggestion correctly identifies that the original validation logic not isinstance(value, Sequence) or isinstance(value, str) is flawed because a string is a sequence, causing the first part of the or to be false and the second to be true, thus always raising an error for strings. The proposed change isinstance(value, str) or not isinstance(value, Sequence) correctly handles this by checking for strings first.


[general] Convert sequence to list consistently

✅ 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 importance[1-10]: 7

__

Why: The suggestion correctly points out an inconsistency. The __init__ method initializes self._service_args as a list and uses .append(), but the setter allows any Sequence to be assigned. This could lead to runtime errors if a non-list sequence (like a tuple) is assigned and subsequent code expects a list. Converting to a list in the setter ensures type consistency for the internal attribute.


[general] Add string exclusion validation consistency

✅ 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 importance[1-10]: 9

__

Why: This suggestion correctly points out two issues: the inconsistent validation that allows strings, and the need to convert the input to a list. Fixing this prevents potential errors and aligns the chrome service's behavior with all other service classes modified in this PR, improving overall consistency and robustness.


[general] Convert sequence to list consistently

✅ 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 importance[1-10]: 8

__

Why: The suggestion correctly identifies that _service_args should consistently be a list, as it is initialized as one and other parts of the codebase may rely on its mutability (e.g., using .append()). Assigning a generic Sequence like a tuple could lead to runtime errors.


[incremental [*]] Convert sequence to list assignment

✅ 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 importance[1-10]: 6

__

Why: The suggestion correctly points out a potential issue. The __init__ method initializes _service_args as a list, and other methods in related classes mutate it using methods like .append(). The setter should also convert the incoming sequence to a list to maintain internal type consistency and prevent potential AttributeError if an immutable sequence (like a tuple) is passed.


[general] Add string exclusion validation consistency

✅ 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 importance[1-10]: 8

__

Why: The suggestion correctly identifies two issues: the validation for service_args is incomplete as it allows strings, and the assigned value is not converted to a list. The proposed change aligns the implementation with other service classes in the PR, improving both robustness and consistency.


[general] Improve validation and conversion consistency

✅ 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 importance[1-10]: 8

__

Why: The suggestion correctly identifies that the service_args setter is missing validation to exclude strings and does not convert the input sequence to a list. Applying this change makes the setter more robust and consistent with other service classes modified in this PR.


[incremental [*]] Convert sequence to list

✅ 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 importance[1-10]: 6

__

Why: The suggestion correctly identifies that self._service_args should be a mutable list, as it's modified in the __init__ method. Converting the assigned value to a list in the setter ensures this, improving robustness and consistency with the initialization logic.


[general] Ensure service args mutability

✅ 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 importance[1-10]: 8

__

Why: This is a valuable suggestion. The code later appends to self._service_args, which requires a mutable sequence. If an immutable sequence like a tuple is passed for service_args, it would cause a runtime AttributeError. Converting to a list ensures mutability and prevents this bug.


[general] Prevent strings as sequence arguments

✅ 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

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that a string, being a sequence, would pass the isinstance(value, Sequence) check but is likely not the intended input for a list of arguments. Adding a check for isinstance(value, str) makes the validation more robust and prevents potential runtime errors.


[possible issue] Fix inconsistent attribute assignment

✅ Fix inconsistent attribute assignment

The service_args assignment should use the private attribute _service_args to be consistent with the property implementation. This ensures the getter/setter pattern works correctly.

py/selenium/webdriver/wpewebkit/service.py [40-49]

 def __init__(
     self,
     executable_path: str = DEFAULT_EXECUTABLE_PATH,
     port: int = 0,
     log_output: Optional[str] = None,
     service_args: Optional[Sequence[str]] = None,
     env: Optional[Mapping[str, str]] = None,
     **kwargs,
 ):
-    self.service_args = service_args or []
+    self._service_args = service_args or []

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies an inconsistency in py/selenium/webdriver/wpewebkit/service.py where self.service_args is used in __init__ instead of self._service_args. While functionally correct due to the setter, changing it improves consistency with other modified files in the PR.



                     PR 15887 (2025-06-10)                    
[general] Handle negative numeric strings properly

✅ Handle negative numeric strings properly

The isdigit() method only checks for positive integers and doesn't handle negative numbers, floats, or other numeric formats. Use a more robust check to determine if the string represents a number that shouldn't be parsed as JSON.

py/selenium/webdriver/remote/errorhandler.py [165]

-if not value_json.isdigit():
+if not (value_json.isdigit() or (value_json.startswith('-') and value_json[1:].isdigit())):

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that isdigit() doesn't handle negative numeric strings. The current code would raise an unhandled TypeError for a string like "-10", because json.loads() would succeed but subsequent operations on the resulting integer would fail. The proposed change makes the numeric check more robust and prevents this crash.



                     PR 15880 (2025-06-09)                    
[possible issue] Validate required JSON fields

✅ 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 importance[1-10]: 7

__

Why: The suggestion correctly points out that the from_json method for RealmInfo does not validate the presence of required fields (realm, origin, type). The RealmInfo dataclass defines these as non-optional strings, so allowing None values from json.get() could lead to downstream errors. Adding validation improves robustness by ensuring the data conforms to the expected structure.



                     PR 15853 (2025-06-04)                    
[possible issue] Fix incorrect constructor parameter

✅ Fix incorrect constructor parameter

The super().init(KEY) call is incorrect. The parent class Interaction expects an input source object, not a string constant. You should pass source (or self.input_source) to the parent constructor instead of KEY.

py/selenium/webdriver/common/actions/key_actions.py [27-31]

 def __init__(self, source: KeyInput | PointerInput | WheelInput | None = None) -> None:
     if source is None:
         source = KeyInput(KEY)
     self.input_source = source
-    super().__init__(KEY)
+    super().__init__(self.input_source)

Suggestion importance[1-10]: 9

__

Why: This identifies a critical bug where super().__init__(KEY) passes a string constant instead of the expected input source object. This would likely cause runtime errors or incorrect behavior in the parent class.



                     PR 15848 (2025-06-03)                    
[incremental [*]] Prevent potential KeyError

✅ 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 importance[1-10]: 8

__

Why: This identifies a real bug where self.subscriptions[event_name] is accessed on line 719 without checking if event_name exists in self.subscriptions, which could cause a KeyError. The improved code correctly moves the length check inside the conditional block.


[general] Add warning for unexpected types

✅ Add warning for unexpected types

The current implementation may silently ignore non-dictionary items in the children list without any warning. Consider adding a warning or log message when encountering unexpected child types to help with debugging.

py/selenium/webdriver/common/bidi/browsing_context.py [111-113]

 raw_children = json.get("children")
 if raw_children is not None and isinstance(raw_children, list):
-    children = [BrowsingContextInfo.from_json(child) for child in raw_children if isinstance(child, dict)]
+    children = []
+    for child in raw_children:
+        if isinstance(child, dict):
+            children.append(BrowsingContextInfo.from_json(child))
+        else:
+            import warnings
+            warnings.warn(f"Unexpected child type in browsing context: {type(child)}")

Suggestion importance[1-10]: 4

__

Why: The suggestion adds debugging warnings for unexpected child types, which could be helpful but is not critical. The PR already improved safety by adding isinstance(child, dict) filtering.


[possible issue] Prevent ValueError on removal

✅ 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 importance[1-10]: 7

__

Why: Valid suggestion that prevents potential ValueError when attempting to remove a non-existent callback_id from the subscriptions list, improving error handling robustness.


[possible issue] Validate dictionary type before processing

✅ Validate dictionary type before processing

Add type checking for each child element before calling from_json to prevent runtime errors. The current code assumes each child is a dictionary, but if any non-dictionary value is present in the list, it will cause a runtime error.

py/selenium/webdriver/common/bidi/browsing_context.py [111-113]

 raw_children = json.get("children")
 if raw_children is not None and isinstance(raw_children, list):
-    children = [BrowsingContextInfo.from_json(child) for child in raw_children]
+    children = [BrowsingContextInfo.from_json(child) for child in raw_children if isinstance(child, dict)]

Suggestion importance[1-10]: 6

__

Why: Valid suggestion to prevent runtime errors when non-dictionary values are in the children list, though this is likely an edge case in normal usage.



Clone this wiki locally