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, osView on GitHub (pinned to 191bd6d754)
Solutions
- Increase the seconds argument to a value that comfortably exceeds the slowest expected run.
- Pass an on_timeout callback to degrade gracefully instead of raising (e.g. return a sentinel / log and skip).
- Diagnose and fix the underlying hang (driver service health, network, deadlocked event loop).
- 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
- Profile decorated functions to pick a realistic timeout, not the 3s default.
- Provide an on_timeout callback for any decorator guarding I/O that can legitimately stall.
- Do not assume the TimeoutError cancels the in-flight call - it fires on a different thread.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- you cannot reuse the ChromeOptions object
- file does not exist: %s
- dont parse timezone format
- dont parse timezone format
- Element {value} not found in any frame or iframe
AI-assisted analysis of NaiboWang/EasySpider@191bd6d754 (2026-08-13).
Data as JSON: /api/errors/9f742f76eb1eb852.
Report an issue: GitHub.