{"record":{"id":"1c19dd2c489fddb4","repo":"cocoindex-io/cocoindex","slug":"cocoindex-timeout-deadline-exceeded-1c19dd","errorCode":null,"errorMessage":"CocoIndex timeout deadline exceeded","messagePattern":"CocoIndex timeout deadline exceeded","errorType":"exception","errorClass":"DeadlineExceededError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/deadline.py","lineNumber":188,"sourceCode":"        raise ValueError(\"retry_transient requires a positive timeout\")\n    if backoff is None:\n        backoff = exponential_backoff()\n\n    scope = _timeout_scope(timeout) if timeout is not None else contextlib.nullcontext()\n    with scope:\n        # Exception, not BaseException: doubles as a type-level guard — if\n        # the except clause below ever widens back to BaseException, this\n        # assignment becomes a mypy error.\n        last_error: Exception | None = None\n        attempt_index = 0\n        while True:\n            # Never start an attempt past the deadline. A remaining time of\n            # exactly zero counts as expired, so a sleep clipped to the\n            # deadline cannot spin at the boundary.\n            check_cancellation()\n            remaining = remaining_seconds()\n            if remaining is not None and remaining <= 0:\n                raise DeadlineExceededError(\"CocoIndex timeout deadline exceeded\")\n\n            try:\n                if bound_attempt and remaining is not None:\n                    try:\n                        result = await asyncio.wait_for(fn(), timeout=remaining)\n                    except TimeoutError as timeout_error:\n                        # Translate only a wait_for cancellation at the\n                        # deadline; a TimeoutError raised by fn itself\n                        # before the deadline re-raises as-is.\n                        remaining_now = remaining_seconds()\n                        if remaining_now is not None and remaining_now <= 0:\n                            raise DeadlineExceededError(\n                                \"CocoIndex timeout deadline exceeded\"\n                            ) from timeout_error\n                        raise\n                else:\n                    result = await fn()\n            except Exception as error:","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/deadline.py#L170-L206","documentation":"DeadlineExceededError is raised by retry_transient when the cooperative deadline has expired: no attempt may start past the deadline, and a remaining time of exactly zero or less counts as expired before each attempt. It can also arise with bound_attempt=True when asyncio.wait_for cancels an in-flight attempt at the deadline. Unlike a retryable failure, this is a policy wall — the operation as a whole has run out of time and the loop stops instead of retrying.","triggerScenarios":"The sum of attempt durations plus backoff sleeps exceeds the effective timeout (either the timeout= argument or an ambient coco.timeout() scope); calling retry_transient when an outer deadline is already nearly exhausted; bound_attempt=True with fn() that ignores deadlines and has to be cancelled at the wall.","commonSituations":"Long-running network/database operations whose transient errors burn the whole budget through repeated retries; nested deadlines where an outer app-level timeout leaves almost nothing for an inner retry loop; slow fn() calls (no awaits that check cancellation) that only get cut off by bound_attempt=True.","solutions":["Increase the timeout= budget or raise the enclosing coco.timeout(timedelta(...)) scope to cover the realistic total time including retries.","Reduce retry pressure: lower max_attempts, increase exponential_backoff initial/max_delay, or narrow retry_on to genuinely transient errors so fatal ones fail fast.","Make fn() cooperative — await checkpointed calls so the deadline check runs inside attempts, and consider bound_attempt=True to hard-cancel long attempts.","Catch DeadlineExceededError at the operation boundary and handle it as a timeout (report, reschedule, or propagate), not as a transient failure to retry."],"exampleFix":"// before\nwith timeout(timedelta(seconds=5)):  # too small for N retries\n    await retry_transient(fn, retry_on=(IOError,), max_attempts=10)\n\n// after\nwith timeout(timedelta(seconds=60)):\n    await retry_transient(fn, retry_on=(IOError,), max_attempts=10, bound_attempt=True)","handlingStrategy":"try-catch","validationCode":"from cocoindex._internal import deadline\nremaining = deadline.remaining_seconds()\nif remaining is not None and remaining <= 0:\n    raise RuntimeError(\"budget already exhausted before starting retries\")","typeGuard":"def has_budget(seconds_needed: float) -> bool:\n    from cocoindex._internal import deadline\n    r = deadline.remaining_seconds()\n    return r is None or r > seconds_needed","tryCatchPattern":"from cocoindex import DeadlineExceededError\ntry:\n    result = await retry_transient(fn, retry_on=(IOError,), max_attempts=5, timeout=timeout)\nexcept DeadlineExceededError:\n    logger.warning(\"operation exceeded its time budget; giving up\")\n    # treat as timeout: reschedule, report, or propagate — do NOT retry","preventionTips":["Size timeout= to cover all attempts plus backoff, not just one attempt","Narrow retry_on so only genuinely transient errors consume budget","Enable bound_attempt=True for fns that may not yield to the deadline check","Catch DeadlineExceededError at the operation boundary and treat it as final, not retryable"],"tags":["python","timeout","retry","deadline"],"backgroundTag":"request-timeout","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}