Skip to content

Script timeout in Chrome Canary with selenium_jspi fixture #163

Description

@illia-v

Hi there,

In the urllib3 CI, we have a job that tests our library against Chrome Canary. For the past few weeks, this job has been consistently failing due to a script timeout. The last successful run was with Chrome 139.0.7249.0, and the first failure we observed was with Chrome 139.0.7254.0.

The timeout appears to be specific to the selenium_jspi fixture, as the tests pass when using the plain selenium fixture. I was able to reproduce this issue by running the pytest-pyodide test suite with Google Chrome 140.0.7301.0.

test_jspi[chrome] details of pytest -v --runtime=chrome
__________________________________________________________________________________ test_jspi[chrome] ___________________________________________________________________________________

self = <pytest_pyodide.runner.SeleniumChromeRunner object at 0x7f24f91be350>
code = '\n        async def __tmp():\n            __tracebackhide__ = True\n\n            from pytest_pyodide.decorator impor...n\n        try:\n            result = await __tmp()\n        finally:\n            del __tmp\n        result\n        '

    def run_async(self, code):
>       return self.run_js(
            f"""
            await pyodide.loadPackagesFromImports({code!r})
            let result = await pyodide.runPythonAsync({code!r});
            return pyodide.$handleTestResult(result);
            """
        )

pytest_pyodide/runner.py:235: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
pytest_pyodide/runner.py:264: in run_js
    return self.run_js_inner(code, check_code)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pytest_pyodide/runner.py:364: in run_js_inner
    retval = self.driver.execute_async_script(wrapper % (code, check_code))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../.virtualenvs/pytest-pyodide/lib/python3.13/site-packages/selenium/webdriver/remote/webdriver.py:426: in execute_async_script
    return self.execute(command, {"script": script, "args": converted_args})["value"]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../.virtualenvs/pytest-pyodide/lib/python3.13/site-packages/selenium/webdriver/remote/webdriver.py:347: in execute
    self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7f24f91bec50>
response = {'status': 500, 'value': '{"value":{"error":"script timeout","message":"script timeout\\n  (Session info: chrome=140.0...3Cunknown>\\n#15 0x5599747d1c13 \\u003Cunknown>\\n#16 0x7f0b05368724 start_thread\\n#17 0x7f0b053ec80c __clone3\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.TimeoutException: Message: script timeout
E         (Session info: chrome=140.0.7301.0)
E       Stacktrace:
E       #0 0x5599747d2c3a <unknown>
E       #1 0x559974278673 <unknown>
E       #2 0x559974317524 <unknown>
E       #3 0x5599742f0222 <unknown>
E       #4 0x559974315ec2 <unknown>
E       #5 0x5599742efff3 <unknown>
E       #6 0x5599742bcbe8 <unknown>
E       #7 0x5599742bd861 <unknown>
E       #8 0x559974797548 <unknown>
E       #9 0x55997479b282 <unknown>
E       #10 0x55997477ea49 <unknown>
E       #11 0x55997479bdc5 <unknown>
E       #12 0x5599747637cf <unknown>
E       #13 0x5599747bfee8 <unknown>
E       #14 0x5599747c00c2 <unknown>
E       #15 0x5599747d1c13 <unknown>
E       #16 0x7f0b05368724 start_thread
E       #17 0x7f0b053ec80c __clone3

../../.virtualenvs/pytest-pyodide/lib/python3.13/site-packages/selenium/webdriver/remote/errorhandler.py:229: TimeoutException

It seems like there might have been a change in recent Chrome Canary 139 & 140 versions that affects the selenium_jspi fixture.

Thanks for your help!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions