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

  1. Pass a positive timedelta, e.g. timedelta(seconds=1) or timedelta(milliseconds=500)
  2. Guard computed intervals: if secs <= 0, fall back to the default (report_to_stdout=True)
  3. 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

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


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