HKUDS/Vibe-Trading · error · ValueError

retry_max_delay_ms must be at least retry_base_delay_ms

Error message

retry_max_delay_ms must be at least retry_base_delay_ms

What it means

The executor enforces retry_max_delay_ms >= retry_base_delay_ms so the backoff cap cannot be below its starting delay — otherwise the exponential sequence would be nonsensical (capped below its first term). Both parameters may also come from tuning defaults, so the check compares the resolved values.

Source

Thrown at agent/src/scheduled_research/executor.py:271

            if max_consecutive_failures is not None
            else tuning.vibe_trading_scheduler_max_consecutive_failures
        )
        self._retry_base_delay_ms = (
            retry_base_delay_ms
            if retry_base_delay_ms is not None
            else tuning.vibe_trading_scheduler_retry_base_delay_ms
        )
        self._retry_max_delay_ms = (
            retry_max_delay_ms
            if retry_max_delay_ms is not None
            else tuning.vibe_trading_scheduler_retry_max_delay_ms
        )
        if self._max_consecutive_failures < 1:
            raise ValueError("max_consecutive_failures must be at least 1")
        if self._retry_base_delay_ms < 0:
            raise ValueError("retry_base_delay_ms must be non-negative")
        if self._retry_max_delay_ms < self._retry_base_delay_ms:
            raise ValueError("retry_max_delay_ms must be at least retry_base_delay_ms")
        self._task: asyncio.Task | None = None
        self._wakeup: asyncio.Event | None = None
        self._stopping = False
        self._recovered_stale_running = False

    @property
    def is_running(self) -> bool:
        """Return whether the background loop task is active."""
        return self._task is not None and not self._task.done()

    def start(self) -> None:
        """Start the background loop.

        Idempotent. When disabled, this is a no-op.
        """
        if not self._enabled or self.is_running:
            return
        self._stopping = False

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Raise retry_max_delay_ms to at least retry_base_delay_ms (typically much larger, e.g. base 1s, max 300s).
  2. When overriding base delay in config, override the max delay in the same place.
  3. Use the defaults unless you specifically need custom backoff.

Example fix

# before
SchedulerExecutor(retry_base_delay_ms=60000, retry_max_delay_ms=5000)
# after
SchedulerExecutor(retry_base_delay_ms=1000, retry_max_delay_ms=300000)
Defensive patterns

Strategy: validation

Validate before calling

retry_base_delay_ms = max(0, int(retry_base_delay_ms))
retry_max_delay_ms = max(retry_base_delay_ms, int(retry_max_delay_ms))
executor = SchedulerExecutor(retry_base_delay_ms=retry_base_delay_ms,
                             retry_max_delay_ms=retry_max_delay_ms)

Prevention

When it happens

Trigger: Constructing with retry_base_delay_ms=60000 and retry_max_delay_ms=5000, or where an explicit base delay overrides a smaller default max delay while the max stays at its tuning default.

Common situations: Setting only one of the two knobs in config: e.g. raising base delay to 60s while the max stays at a 30s default, breaking the invariant.

Understand the failure class

Background: "Invalid configuration value" and "Unsupported/Unknown setting value" errors: why libraries reject your config strings, numbers, and types — this error's family across 30 libraries.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/9589cb1efdf7fd18. Report an issue: GitHub.