BerriAI/litellm · error · ValueError

Unsupported frequency: {self.frequency}

Error message

Unsupported frequency: {self.frequency}

What it means

FocusLogger raises ValueError when building the APScheduler trigger if self.frequency is not one of 'interval', 'hourly', 'daily'. The frequency string selects the trigger shape (interval seconds / hourly cron at cron_offset_minute / daily cron derived from cron_offset_minute); anything else falls through to this raise.

Source

Thrown at litellm/integrations/focus/focus_logger.py:172

        )

    def _build_scheduler_trigger(self) -> dict[str, Any]:
        """Return scheduler configuration for the selected frequency."""
        if self.frequency == "interval":
            seconds: Final = self.interval_seconds or 60
            return {"trigger": "interval", "seconds": seconds}

        if self.frequency == "hourly":
            minute = max(0, min(59, self.cron_offset_minute))
            return {"trigger": "cron", "minute": minute, "second": 0}

        if self.frequency == "daily":
            total_minutes: Final = max(0, self.cron_offset_minute)
            hour: Final = min(23, total_minutes // 60)
            minute = min(59, total_minutes % 60)
            return {"trigger": "cron", "hour": hour, "minute": minute, "second": 0}

        raise ValueError(f"Unsupported frequency: {self.frequency}")

    async def _run_scheduled_export(self) -> None:
        """Execute the scheduled export for the configured window."""
        window: Final = self._compute_time_window(datetime.now(timezone.utc))
        await self._export_window(window=window, limit=None)

    async def _export_all(
        self,
        *,
        limit: int | None,
    ) -> None:
        """Export all available data without a time window filter."""
        engine: Final = self._ensure_engine()
        await engine.export_all(limit=limit)

    async def _export_window(
        self,
        *,

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Use exactly one of 'interval', 'hourly', 'daily' (lowercase).
  2. Validate the value at config load time and fail fast with a clear message listing valid options.
  3. For 'interval', also set interval_seconds.

Example fix

# before
FocusLogger(frequency="weekly")

# after
FocusLogger(frequency="daily")
Defensive patterns

Strategy: validation

Validate before calling

VALID = {"interval", "hourly", "daily"}
freq = (frequency or "").strip().lower()
if freq not in VALID:
    raise ValueError(f"frequency must be one of {sorted(VALID)}, got {frequency!r}")
logger = FocusLogger(frequency=freq, ...)

Type guard

FocusFrequency = Literal["interval", "hourly", "daily"]

def is_focus_frequency(v: object) -> bool:
    return isinstance(v, str) and v in {"interval", "hourly", "daily"}

Prevention

When it happens

Trigger: Constructing FocusLogger(frequency='weekly'|'minute'|'HOURLY'|None) — any string other than the three supported lowercase values reaches the raise when the scheduler trigger dict is built.

Common situations: Case mismatch; a config value copied from cron shorthand; frequency defaulted to empty string by templating; version drift after a rename of supported values.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/cbe3bb12f437d812. Report an issue: GitHub.