cocoindex-io/cocoindex · error · ValueError
report_to_stdout interval must be a positive duration
Error message
report_to_stdout interval must be a positive duration
What it means
When report_to_stdout is given as a timedelta to update_blocking/drop_blocking/stats_group, the interval must be strictly positive; zero or negative durations cannot drive progress reporting. _resolve_report_to_stdout validates this before starting the update.
Source
Thrown at python/cocoindex/_internal/update_stats.py:26
R = TypeVar("R")
_TERMINATED_VERSION = 2**64 - 1 # u64::MAX
def _resolve_report_to_stdout(
report_to_stdout: bool | timedelta,
) -> tuple[bool, float | None]:
"""Normalize a ``bool | timedelta`` progress flag into
``(enabled, refresh_interval_secs)`` for the core boundary.
- ``False`` → ``(False, None)`` (no report)
- ``True`` → ``(True, None)`` (report at the default interval)
- ``timedelta`` → ``(True, secs)`` (report at that interval; must be positive)
"""
if isinstance(report_to_stdout, timedelta):
secs = report_to_stdout.total_seconds()
if secs <= 0:
raise ValueError("report_to_stdout interval must be a positive duration")
return True, secs
return bool(report_to_stdout), None
class _CoreStatsHandle(Protocol):
"""Structural interface shared by the core update / drop / stats-group
handles — everything `_StatsView` needs to read progress."""
def stats_snapshot(self) -> tuple[int, bool, dict[str, dict[str, int]]]: ...
def changed(self) -> Coroutine[Any, Any, int]: ...
H = TypeVar("H", bound=_CoreStatsHandle)
def _decode_update_stats(raw: dict[str, dict[str, int]]) -> UpdateStats:
"""Decode the raw `{processor: {field: value}}` snapshot from core."""
return UpdateStats(View on GitHub (pinned to e84aa99b32)
Solutions
- Pass a positive timedelta, e.g. timedelta(seconds=1) or timedelta(milliseconds=500)
- Guard computed intervals: if secs <= 0, fall back to the default (report_to_stdout=True)
- Use report_to_stdout=True for the default interval instead of an explicit duration
Example fix
// before app.update_blocking(report_to_stdout=timedelta(0)) // after interval = max(timedelta(seconds=1), computed_interval) app.update_blocking(report_to_stdout=interval)
Defensive patterns
Strategy: validation
Validate before calling
if isinstance(report_to_stdout, timedelta) and report_to_stdout.total_seconds() <= 0:
report_to_stdout = True # fall back to default interval Type guard
def is_valid_interval(d: timedelta) -> bool:
return d.total_seconds() > 0 Try / catch
try:
app.update_blocking(report_to_stdout=interval)
except ValueError as e:
app.update_blocking(report_to_stdout=True) # default interval Prevention
- Never pass timedelta(0) — the default constructor is zero
- Clamp computed intervals to a positive minimum (e.g. 1s)
- Prefer report_to_stdout=True unless a custom cadence is required
When it happens
Trigger: Calling app.update_blocking(report_to_stdout=timedelta(0)), a negative timedelta, or a timedelta(seconds=0) derived from computation, to any of update_blocking, drop_blocking, or stats_group.
Common situations: Computing an interval dynamically (e.g. total_time/2) that evaluates to 0 for fast runs; passing timedelta() default which is zero; sign errors when constructing the interval.
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
- Cannot specify both an S3 URI and a separate key. Pass eithe
- key must be provided when bucket_name_or_uri is not an S3 UR
- expected None{loc}, got {type(value).__name__}
- expected {tp}{loc}, got {type(value).__name__}: {value!r}
- expected tuple{loc}, got {type(value).__name__}
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/adbf7b2e1adbc83c.
Report an issue: GitHub.