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

  1. Wrap the numeric value: use datetime.timedelta(seconds=30) instead of 30.
  2. If the value comes from config, parse it into a timedelta at the boundary before entering the timeout scope.
  3. 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

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.

Related errors


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/867062a327e2d643. Report an issue: GitHub.