Selenium Marionette driver UnreachableBrowserException upon second launch
After digging a little deeper, I came to the following findings which eventually solved my issue:
Marionette resp.
wires
uses two ports (seewires --help
); amarionette-port
and awebdriver-port
:Usage: ./wires [OPTIONS]WebDriver to marionette proxy.optional arguments: -h,--help show this help message and exit -b,--binary BINARY Path to the Firefox binary --webdriver-host WEBDRIVER_HOST Host to run webdriver server on --webdriver-port WEBDRIVER_PORT Port to run webdriver on --marionette-port MARIONETTE_PORT Port to run marionette on --connect-existing Connect to an existing firefox process
When running multiple
MarionetteDrivers
simultaneously, both ports have to be different from the already running instance obviously. However, when using the default constructornew MarionetteDriver()
themarionette-port
remains constant (and is not determined based on some free port). We used some workaround (see below) for theGeckoDriverService.Builder
to always pick two randomly-chosen available ports.The current (version 2.48.2)
GeckoDriverService
has an empty implementation ofwaitUntilAvailable()
(which should check, if theWebDriver
is ready to go). Sporadically, this lead to theUnreachableBrowserException
posted above.
To circumvent these issues, we did something like this at the end:
// determine free ports for Marionette and WebDriverfinal int marionettePort = PortProber.findFreePort();final int webDriverPort = PortProber.findFreePort();// override, as GeckoDriverService provides no direct way to set the Marionette portGeckoDriverService.Builder builder = new GeckoDriverService.Builder() { @Override protected ImmutableList<String> createArgs() { Builder<String> argsBuilder = ImmutableList.builder(); argsBuilder.addAll(super.createArgs()); argsBuilder.add(String.format("--marionette-port=%d", marionettePort)); return argsBuilder.build(); }};builder.usingPort(webDriverPort);builder.usingDriverExecutable(pathToDriver);GeckoDriverService driverService = builder.build();try { driverService.start();} catch (IOException e) { throw new IllegalStateException("Could not start the GeckoDriverService", e);}try { // keep checking the WebDriver port via Socket until it's available; // as far as I could tell, there is nothing more "high level", e.g. REST API waitUntilReady(webDriverPort, TimeUnit.SECONDS.toMillis(30));} catch (InterruptedException e) { // ignore}return new MarionetteDriver(driverService, capabilities);
You need to handle the binary required by Marionette. You can do it by hand (download the binary on your own and export the proper variable), or you can do it automatically using WebDriverManager. Simply add the following dependency:
<dependency> <groupId>io.github.bonigarcia</groupId> <artifactId>webdrivermanager</artifactId> <version>1.6.0</version></dependency>
And then, in your code call to:
FirefoxDriverManager.getInstance().setup();
Mostly UnreachableBrowserException
happens when Selenium couldn't find the required exe of the driver. Since you haven't put driver capabilities setup, I am assuming the following steps will solve your issue.
As per Mozilla MDN link Marionette is set as a DesiredCapability
DesiredCapabilities capabilities = DesiredCapabilities.firefox();// Set Marionette on so the Grid will use this instead of normal FirefoxDrivercapabilities.setCapability("marionette", true);WebDriver driver = new RemoteWebDriver(capabilities);
Also, Marionette executable is added to the path (In Windows):
Adding the executable to the PATH
Selenium will try use the executable in the path. You will need to add it to the path using the following.
Finally, another SO question where UnreachableBrowserException issue is handled.