NaiboWang/EasySpider · error · TimeoutError

function call timed out

Error message

function call timed out

What it means

Raised as TimeoutError by the `timeout` decorator in ExecuteStage/undetected_chromedriver_ES/devtool.py:86 when a decorated function has not returned within `seconds` (default 3). IMPORTANT threading caveat: the timer callback runs in a separate threading.Timer thread, so the TimeoutError is raised in THAT thread, not in the caller's thread which is still executing func. Unless the caller also surfaces timer-thread exceptions (e.g. via a shared queue/future), the exception may be logged but not actually interrupt the running call.

Source

Thrown at ExecuteStage/undetected_chromedriver_ES/devtool.py:86

    @classmethod
    def __init_subclass__(cls, **kwargs):
        cls._store = {}

    def _normalize_strings(self):
        for k, v in self.copy().items():
            if isinstance(v, (str)):
                self[k] = v.strip()


def timeout(seconds=3, on_timeout: Optional[Callable[[callable], Any]] = None):
    def wrapper(func):
        @wraps(func)
        def wrapped(*args, **kwargs):
            def function_reached_timeout():
                if on_timeout:
                    on_timeout(func)
                else:
                    raise TimeoutError("function call timed out")

            t = threading.Timer(interval=seconds, function=function_reached_timeout)
            t.start()
            try:
                return func(*args, **kwargs)
            except:
                t.cancel()
                raise
            finally:
                t.cancel()

        return wrapped

    return wrapper


def test():
    import sys, os

View on GitHub (pinned to 191bd6d754)

Solutions

  1. Increase the seconds argument to a value that comfortably exceeds the slowest expected run.
  2. Pass an on_timeout callback to degrade gracefully instead of raising (e.g. return a sentinel / log and skip).
  3. Diagnose and fix the underlying hang (driver service health, network, deadlocked event loop).
  4. Be aware the TimeoutError fires in the timer thread; if you need to actually cancel the call, run func in a subprocess or use a cancelable primitive instead of this decorator.

Example fix

# before
@timeout(seconds=3)
def poll(driver):
    return driver.get_log('performance')

# after - longer window + graceful callback
@timeout(seconds=15, on_timeout=lambda fn: [])
def poll(driver):
    return driver.get_log('performance')
Defensive patterns

Strategy: try-catch

Try / catch

from devtool import timeout  # or wherever imported
try:
    result = poll(driver)
except TimeoutError as e:
    if 'function call timed out' in str(e):
        result = []  # or retry / log
    else:
        raise

Prevention

When it happens

Trigger: A function decorated with @timeout(seconds=N) (or called via the decorator factory with on_timeout=None) runs longer than N seconds; the Timer fires function_reached_timeout() which raises TimeoutError('function call timed out'). If on_timeout is provided it is called instead.

Common situations: CDP log polling (driver.get_log) blocks because the driver service is unresponsive; network hang talking to chromedriver; the decorated function waits on an event that never arrives; default 3s is too short for slow pages.

Understand the failure class

Related errors


AI-assisted analysis of NaiboWang/EasySpider@191bd6d754 (2026-08-13). Data as JSON: /api/errors/9f742f76eb1eb852. Report an issue: GitHub.