Android appium tests fail with "FindElementModel: The mandatory field 'selector' is not present in JSON" (solved)

Since a few days I cannot run any Appium tests on my Android simulator. Every time a findElement request is handled, the “The mandatory field ‘selector’ is not present in JSON” error appears.

I’ve been trying to fix this issue all day now, but I am lost.

The issue is that Appium sends a valid request to the driver:

[HTTP] → POST http://localhost:4723/wd/hub/session/475495aa-2e80-41bf-8881-d132cca2f091/element
[HTTP] {“using”:“id”,“value”:“btnContinue”}

And the uiautomator2 driver responds with:

[AndroidUiautomator2Driver@7b3b (475495aa)] Driver proxy active, passing request on via HTTP proxy
[AndroidUiautomator2Driver@7b3b (475495aa)] Matched ‘http://localhost:4723/wd/hub/session/475495aa-2e80-41bf-8881-d132cca2f091/element’ to command name ‘findElement’
[AndroidUiautomator2Driver@7b3b (475495aa)] Proxying [POST http://localhost:4723/wd/hub/session/475495aa-2e80-41bf-8881-d132cca2f091/element] to [POST http://127.0.0.1:8200/session/c52f8af1-a42a-4409-bb7c-a45fff393c91/element] with body: {“using”:“id”,“value”:“btnContinue”}
[AndroidUiautomator2Driver@7b3b (475495aa)] Got response with status 400: {“sessionId”:“c52f8af1-a42a-4409-bb7c-a45fff393c91”,“value”:{“error”:“invalid argument”,“message”:“java.lang.IllegalArgumentException: FindElementModel: The mandatory field ‘selector’ is not present in JSON”,“stacktrace”:"io.appium.uiautomator2.common.exceptions.InvalidArgumentException: java.lang.IllegalArgumentException: FindElementModel: The mandatory field ‘selector’ is not present in JSON\n\tat io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:65)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleRequest(AppiumServlet.java:259)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:253)\n\tat io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:77)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)\n\tat io.netty.channel.AbstractChannelHa…

Appium version: 2.5.1
uiautomator2 driver: 3.0.4

Also tried other Appium and driver versions.

Here is the Appium log:

Appium.logt (49.5 KB)

Can you find the element in the app’s XML or when using Appium Inspector? What library you use By (selenium) or AppiumBy (appium). If possible please post a small code snippet of where the code fails so we can see your actual code and approach

This is not the issue in this case. When the findElement command is issued, the error occurs whether the element is visible on the screen or not:

[HTTP] --> POST http://localhost:4723/session/ae0cc1ad-d4b0-4321-b3ea-b325a397ea5c/element
[HTTP] {"using":"id","value":"btnContinue"}
[AndroidUiautomator2Driver@d6bb (ae0cc1ad)] Driver proxy active, passing request on via HTTP proxy
[AndroidUiautomator2Driver@d6bb (ae0cc1ad)] Matched 'http://localhost:4723/session/ae0cc1ad-d4b0-4321-b3ea-b325a397ea5c/element' to command name 'findElement'
[AndroidUiautomator2Driver@d6bb (ae0cc1ad)] Proxying [POST http://localhost:4723/session/ae0cc1ad-d4b0-4321-b3ea-b325a397ea5c/element] to [POST http://127.0.0.1:8200/session/24f59b80-cfe3-485b-a679-782c323fe0f3/element] with body: {"using":"id","value":"btnContinue"}
[AndroidUiautomator2Driver@d6bb (ae0cc1ad)] Got response with status 400: {"sessionId":"24f59b80-cfe3-485b-a679-782c323fe0f3","value":{"error":"invalid argument","message":"java.lang.IllegalArgumentException: FindElementModel: The mandatory field 'selector' is not present in JSON","stacktrace":"io.appium.uiautomator2.common.exceptions.InvalidArgumentException: java.lang.IllegalArgumentException: FindElementModel: The mandatory field 'selector' is not present in JSON\n\tat io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:65)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleRequest(AppiumServlet.java:259)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:253)\n\tat io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:77)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)\n\tat io.netty.channel.AbstractChannelHa...

When comparing this with the Appium log on a build slave (which has no issues), I see the following:

2024-03-26 22:14:40:893 [HTTP] --> POST /session/548ee79c-475c-4f38-ae3b-43140e13a3e5/element
2024-03-26 22:14:40:894 [HTTP] {"using":"id","value":"btnContinue"}
2024-03-26 22:14:40:895 [AndroidUiautomator2Driver@8091 (548ee79c)] Calling AppiumDriver.findElement() with args: ["id","btnContinue","548ee79c-475c-4f38-ae3b-43140e13a3e5"]
2024-03-26 22:14:40:896 [AndroidUiautomator2Driver@8091 (548ee79c)] Valid locator strategies for this request: xpath, id, class name, accessibility id, css selector, -android uiautomator
2024-03-26 22:14:40:896 [AndroidUiautomator2Driver@8091 (548ee79c)] Waiting up to 0 ms for condition
2024-03-26 22:14:40:897 [AndroidUiautomator2Driver@8091 (548ee79c)] Matched '/element' to command name 'findElement'
2024-03-26 22:14:40:898 [AndroidUiautomator2Driver@8091 (548ee79c)] Proxying [POST /element] to [POST http://127.0.0.1:8200/session/fd67a4cc-91df-47fc-a100-4188e5d2e3ba/element] with body: {"strategy":"id","selector":"btnContinue","context":"","multiple":false}
2024-03-26 22:14:40:941 [AndroidUiautomator2Driver@8091 (548ee79c)] Got response with status 200: {"sessionId":"fd67a4cc-91df-47fc-a100-4188e5d2e3ba","value":{"ELEMENT":"00000000-0000-0008-ffff-ffff00000042","element-6066-11e4-a52e-4f735466cecf":"00000000-0000-0008-ffff-ffff00000042"}}
2024-03-26 22:14:40:941 [AndroidUiautomator2Driver@8091 (548ee79c)] Responding to client with driver.findElement() result: {"element-6066-11e4-a52e-4f735466cecf":"00000000-0000-0008-ffff-ffff00000042","ELEMENT":"00000000-0000-0008-ffff-ffff00000042"}

The difference here is that on the build slave, the AppiumDriver is called:

2024-03-26 22:14:40:895 [AndroidUiautomator2Driver@8091 (548ee79c)] Calling AppiumDriver.findElement() with args: ["id","btnContinue","548ee79c-475c-4f38-ae3b-43140e13a3e5"]

Which actually modifies the json by adding/updating some keys in the JSON:

2024-03-26 22:14:40:898 [AndroidUiautomator2Driver@8091 (548ee79c)] Proxying [POST /element] to [POST http://127.0.0.1:8200/session/fd67a4cc-91df-47fc-a100-4188e5d2e3ba/element] with body: {"strategy":"id","selector":"btnContinue","context":"","multiple":false}

On my machine the AppiumDriver is not called and the JSON is proxied as-is:

[AndroidUiautomator2Driver@d6bb (ae0cc1ad)] Proxying [POST http://localhost:4723/session/ae0cc1ad-d4b0-4321-b3ea-b325a397ea5c/element] to [POST http://127.0.0.1:8200/session/24f59b80-cfe3-485b-a679-782c323fe0f3/element] with body: {"using":"[HTTP] --> POST http://localhost:4723/session/ae0cc1ad-d4b0-4321-b3ea-b325a397ea5c/element
[HTTP] {"using":"id","value":"btnContinue"}

The executed test and test environment is exactly the same on the build slave and on my machine. Appium version, driver version, test environment are also exactly the same.

The main difference is that the build slave is a Mac mini with an Intel processor and my own machine is a M3 pro MacBook.

When looking at the logs, I see some slight differences.

On the Intel Mac I see a POST request for the findElement as

[HTTP] --> POST /session/548ee79c-475c-4f38-ae3b-43140e13a3e5/element
[HTTP] {"using":"id","value":"btnContinue"}

On the M3 pro mac this looks like:

[HTTP] --> POST http://localhost:4723/session/ae0cc1ad-d4b0-4321-b3ea-b325a397ea5c/element
[HTTP] {"using":"id","value":"btnContinue"}

So currently I am trying to find out why on the m3 pro MacBook the findElement request is not forwarded to the AppiumDriver, but immediately proxied forward.

The code that causes this issue is:

   def _explicitly_wait_for(self, element_id, type="id", wait_time=30, root=None):
        """
        Search for an element on the screen, wait at most <wait_time> until the element is found

        :param element_id:  The identifier of the requested element
        :param type:        The locator type passed with argument 'element_id'
                                "id":       Handle resource 'name' as an identifier (default)
                                "xpath":    Handle resource 'name' as an xpath
                                "class":    Handle resource 'name' as a class
        :param wait_time:   The amount of time to wait until the element is found
        :return:
        """
        logger.info("_explicitly_wait_for(%s, %s, %s, %s)" % (str(element_id), str(type), str(wait_time), str(root)))

        locator = self._get_locator_type(type)
        if root is None:
            context = self.driver()
        else:
            context = root

        return WebDriverWait(context, wait_time).until \
            (expected_conditions.presence_of_element_located((locator, element_id)))

So this error happens only when trying to find btnContinue?

No, it happens on every attempt to find an element on the screen of the Android app (or actually during the WebDriverWait)

When the test starts up, the app is started which may end up in several different screens, depending on how the user closed the app or whether the user is logged in or not.

The test environment checks which screen is visible by analysing the elements on the screen (getting the screen source actually functions) and then takes action depending on the visible screen (tap a login button or tap a continue button) This to eventually end up in the screen where the actual test should be started.

But it does not matter which element is searched. The exception occurs during the findElement request.

The same test is performed for iOS, using the xcuitest Appium driver. This runs without any issue. This exception only happens when testing the Android app.

Finally found the problem. It was the proxy client…

Using a different proxy client the POST http://localhost:4723/session requests changed into the expected POST /session requests.

1 Like

awesome. Happy you found the issue. Happy Testing!