{"record":{"id":"867062a327e2d643","repo":"cocoindex-io/cocoindex","slug":"timeout-requires-a-datetime-timedelta","errorCode":null,"errorMessage":"timeout() requires a datetime.timedelta","messagePattern":"timeout\\(\\) requires a datetime\\.timedelta","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/_internal/deadline.py","lineNumber":39,"sourceCode":"_current_deadline: ContextVar[DeadlineContext] = ContextVar(\n    \"coco_deadline\", default=core.deadline_none()\n)\n\n_logger = logging.getLogger(__name__)\n\n_RetryResultT = TypeVar(\"_RetryResultT\")\n\n# Which failures are safe to retry: an exception-type tuple (isinstance\n# semantics, like an `except` clause) or a predicate for classifications\n# that inspect status codes or messages.\nRetryOn: TypeAlias = tuple[type[Exception], ...] | Callable[[Exception], bool]\n\n\n@contextlib.contextmanager\ndef timeout(duration: timedelta) -> Iterator[None]:\n    \"\"\"Apply a cooperative timeout deadline to CocoIndex checkpoints.\"\"\"\n    if not isinstance(duration, timedelta):\n        raise TypeError(\"timeout() requires a datetime.timedelta\")\n\n    token = _current_deadline.set(\n        _current_deadline.get().with_timeout(duration.total_seconds())\n    )\n    try:\n        yield\n    finally:\n        _current_deadline.reset(token)\n\n\ndef check_cancellation() -> None:\n    \"\"\"Raise if the current work has been asked to stop.\n\n    Deadline expiry is the first cancellation source (raising\n    ``DeadlineExceededError``); future sources (e.g. a batched call whose\n    callers have all been cancelled) will surface through the same\n    checkpoint.\n    \"\"\"","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/_internal/deadline.py#L21-L57","documentation":"timeout() applies a cooperative deadline to CocoIndex checkpointed work and validates that the duration is a datetime.timedelta. Passing any other numeric or string duration (int, float seconds, None) raises this TypeError immediately. The library intentionally refuses implicit conversions so callers always express durations explicitly.","triggerScenarios":"Calling coco timeout(...) (the deadline context manager in python/cocoindex/_internal/deadline.py:36) with e.g. timeout(30), timeout(0.5), timeout('30s'), or a pandas/numpy Timedelta instead of datetime.timedelta(seconds=30).","commonSituations":"Developers porting from libraries whose timeout APIs take plain seconds; writing retry helpers that pass through a user-supplied duration without normalizing; config values read as ints from YAML/env being fed straight into the timeout context manager.","solutions":["Wrap the numeric value: use datetime.timedelta(seconds=30) instead of 30.","If the value comes from config, parse it into a timedelta at the boundary before entering the timeout scope.","If you have a non-standard duration object (pandas Timedelta, isodate Duration), convert via timedelta(seconds=obj.total_seconds())."],"exampleFix":"// before\nwith timeout(30):\n    ...\n\n// after\nfrom datetime import timedelta\nwith timeout(timedelta(seconds=30)):\n    ...","handlingStrategy":"type-guard","validationCode":"from datetime import timedelta\ndef ensure_timedelta(d):\n    if isinstance(d, (int, float)):\n        return timedelta(seconds=d)\n    if not isinstance(d, timedelta):\n        raise TypeError(f\"expected timedelta, got {type(d).__name__}\")\n    return d","typeGuard":"def is_timedelta(v: object) -> bool:\n    from datetime import timedelta\n    return isinstance(v, timedelta)","tryCatchPattern":"try:\n    with timeout(duration):\n        ...\nexcept TypeError as e:\n    if 'datetime.timedelta' in str(e):\n        duration = timedelta(seconds=float(duration))\n        with timeout(duration): ...\n    else:\n        raise","preventionTips":["Always construct durations with datetime.timedelta, never bare numbers","Normalize config/env values to timedelta at the parsing boundary","Add a runtime assertion isinstance(duration, timedelta) in wrapper helpers you write around timeout()"],"tags":["python","timeout","type-error"],"backgroundTag":"invalid-duration-format","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"}