{"record":{"id":"96f0ff9ec3decda2","repo":"infiniflow/ragflow","slug":"function-func-name-timed-out-after-second","errorCode":null,"errorMessage":"Function '{func.__name__}' timed out after {seconds} seconds and {attempts} attempts.","messagePattern":"Function '(.+?)' timed out after (.+?) seconds and (.+?) attempts\\.","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"common/connection_utils.py","lineNumber":61,"sourceCode":"                except Exception as e:\n                    result_queue.put(e)\n\n            thread = threading.Thread(target=target)\n            thread.daemon = True\n            thread.start()\n\n            for a in range(attempts):\n                try:\n                    if os.environ.get(\"ENABLE_TIMEOUT_ASSERTION\"):\n                        result = result_queue.get(timeout=seconds)\n                    else:\n                        result = result_queue.get()\n                    if isinstance(result, Exception):\n                        raise result\n                    return result\n                except queue.Empty:\n                    pass\n            raise TimeoutError(f\"Function '{func.__name__}' timed out after {seconds} seconds and {attempts} attempts.\")\n\n        @wraps(func)\n        async def async_wrapper(*args, **kwargs) -> Any:\n            if seconds is None:\n                return await func(*args, **kwargs)\n\n            for a in range(attempts):\n                try:\n                    if os.environ.get(\"ENABLE_TIMEOUT_ASSERTION\"):\n                        return await asyncio.wait_for(func(*args, **kwargs), timeout=seconds)\n                    else:\n                        return await func(*args, **kwargs)\n                except asyncio.TimeoutError:\n                    if a < attempts - 1:\n                        continue\n                    if on_timeout is not None:\n                        if callable(on_timeout):\n                            result = on_timeout()","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/connection_utils.py#L43-L79","documentation":"TimeoutError raised by the sync branch of the @timeout decorator in common/connection_utils.py:36-61. The decorator runs the wrapped function in a daemon thread and collects its result via a queue; with ENABLE_TIMEOUT_ASSERTION set, each of `attempts` tries waits up to `seconds` for the result, and if the queue never yields one, the wrapper raises this TimeoutError naming the function, seconds, and attempts. Without ENABLE_TIMEOUT_ASSERTION the sync path blocks indefinitely and this error cannot fire.","triggerScenarios":"Calling a sync function decorated with @timeout(seconds=N, attempts=M) with ENABLE_TIMEOUT_ASSERTION in the environment, where the underlying call blocks longer than N seconds on every attempt — typical for Redis/DB/network calls that hang. The thread itself keeps running (daemon, abandoned); only the caller gives up.","commonSituations":"Redis or MySQL unreachable/black-holing packets (no RST, just dropped) so a socket read hangs; test suites setting ENABLE_TIMEOUT_ASSERTION to keep CI fast; slow downstream LLM/API calls exceeding the configured budget; DNS hangs.","solutions":["Diagnose the underlying dependency the function contacts (redis-cli ping, mysql connect, curl the API) — the timeout is a symptom, not the fault.","Raise the decorator's seconds/attempts for legitimately slow operations.","Fix network-level issues: firewall dropping SYNs, wrong host/port, DNS misconfiguration.","Note the abandoned daemon thread still holds resources; avoid retry storms that accumulate threads."],"exampleFix":"# before\n@timeout(seconds=5)\ndef fetch(): ...  # hangs on dead redis\n# after\n@timeout(seconds=30, attempts=3)\ndef fetch(): ...  # plus fix redis host/port","handlingStrategy":"retry","validationCode":"import socket\ndef endpoint_reachable(host: str, port: int, timeout: float = 2.0) -> bool:\n    try:\n        socket.create_connection((host, port), timeout=timeout).close()\n        return True\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"from common.connection_utils import timeout\ntry:\n    result = fetch_via_decorator()\nexcept TimeoutError as e:\n    # decorator already retried `attempts` times; escalate to circuit breaker / alert\n    log.error(\"%s\", e)\n    raise","preventionTips":["Set realistic seconds/attempts per dependency (DB vs LLM differ by orders of magnitude).","Verify host/port reachability before long-running jobs.","Remember the sync wrapper abandons a daemon thread per timeout — avoid tight outer retry loops."],"tags":["timeout","network","decorator","threading","redis"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}