cocoindex-io/cocoindex · error · TypeError
timeout() requires a datetime.timedelta
Error message
timeout() requires a datetime.timedelta
What it means
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.
Source
Thrown at python/cocoindex/_internal/deadline.py:39
_current_deadline: ContextVar[DeadlineContext] = ContextVar(
"coco_deadline", default=core.deadline_none()
)
_logger = logging.getLogger(__name__)
_RetryResultT = TypeVar("_RetryResultT")
# Which failures are safe to retry: an exception-type tuple (isinstance
# semantics, like an `except` clause) or a predicate for classifications
# that inspect status codes or messages.
RetryOn: TypeAlias = tuple[type[Exception], ...] | Callable[[Exception], bool]
@contextlib.contextmanager
def timeout(duration: timedelta) -> Iterator[None]:
"""Apply a cooperative timeout deadline to CocoIndex checkpoints."""
if not isinstance(duration, timedelta):
raise TypeError("timeout() requires a datetime.timedelta")
token = _current_deadline.set(
_current_deadline.get().with_timeout(duration.total_seconds())
)
try:
yield
finally:
_current_deadline.reset(token)
def check_cancellation() -> None:
"""Raise if the current work has been asked to stop.
Deadline expiry is the first cancellation source (raising
``DeadlineExceededError``); future sources (e.g. a batched call whose
callers have all been cancelled) will surface through the same
checkpoint.
"""View on GitHub (pinned to e84aa99b32)
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()).
Example fix
// before
with timeout(30):
...
// after
from datetime import timedelta
with timeout(timedelta(seconds=30)):
... Defensive patterns
Strategy: type-guard
Validate before calling
from datetime import timedelta
def ensure_timedelta(d):
if isinstance(d, (int, float)):
return timedelta(seconds=d)
if not isinstance(d, timedelta):
raise TypeError(f"expected timedelta, got {type(d).__name__}")
return d Type guard
def is_timedelta(v: object) -> bool:
from datetime import timedelta
return isinstance(v, timedelta) Try / catch
try:
with timeout(duration):
...
except TypeError as e:
if 'datetime.timedelta' in str(e):
duration = timedelta(seconds=float(duration))
with timeout(duration): ...
else:
raise Prevention
- 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()
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- retry_transient requires a positive timeout
- CocoIndex timeout deadline exceeded
- {type(obj).__name__} cannot be used as a memoization key. Th
- Unsupported type for memoization key: {type(obj)!r}. Provide
- record_type must be a record type (dataclass, NamedTuple, Py
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/867062a327e2d643.
Report an issue: GitHub.