BerriAI/litellm · error · ValueError
start_time_utc and end_time_utc must be provided together
Error message
start_time_utc and end_time_utc must be provided together
What it means
FocusLogger.export_usage_data raises ValueError when exactly one of start_time_utc / end_time_utc is passed. Time bounds only make sense as a pair (they become the FocusTimeWindow); the check is a boolean XOR, so passing either bound alone is rejected before any DB/export work happens.
Source
Thrown at litellm/integrations/focus/focus_logger.py:92
)
return self._engine
async def export_usage_data(
self,
*,
limit: int | None = None,
start_time_utc: datetime | None = None,
end_time_utc: datetime | None = None,
) -> None:
"""Public hook to trigger export immediately.
When called without time bounds (manual /vantage/export with no
start/end), exports **all** available data instead of the last
scheduled window. The hourly/daily window only applies to
automatic scheduler runs.
"""
if bool(start_time_utc) ^ bool(end_time_utc):
raise ValueError("start_time_utc and end_time_utc must be provided together")
if start_time_utc and end_time_utc:
window: Final = FocusTimeWindow(
start_time=start_time_utc,
end_time=end_time_utc,
frequency=self.frequency,
)
await self._export_window(window=window, limit=limit)
else:
# No time bounds → export all available data
await self._export_all(limit=limit)
async def dry_run_export_usage_data(self, limit: int | None = DEFAULT_DRY_RUN_LIMIT) -> dict[str, Any]:
"""Return transformed data without uploading."""
engine: Final = self._ensure_engine()
return await engine.dry_run_export_usage_data(limit=limit)
async def initialize_focus_export_job(self) -> None:View on GitHub (pinned to 6c2dcb801b)
Solutions
- Pass both bounds together: export_usage_data(start_time_utc=start, end_time_utc=end).
- Or pass neither to export all available data.
- In HTTP handlers, default missing params so both are None or both are set (validate at the request boundary).
Example fix
# before
await logger.export_usage_data(start_time_utc=start)
# after
if start and end:
await logger.export_usage_data(start_time_utc=start, end_time_utc=end)
else:
await logger.export_usage_data() # export all Defensive patterns
Strategy: validation
Validate before calling
if (start_time_utc is None) != (end_time_utc is None):
raise ValueError("Provide both start_time_utc and end_time_utc, or neither")
await logger.export_usage_data(limit=limit, start_time_utc=start_time_utc, end_time_utc=end_time_utc) Type guard
def is_valid_window(start: datetime | None, end: datetime | None) -> bool:
return (start is None) == (end is None) Prevention
- Validate optional start/end pairs at the HTTP boundary before calling export APIs.
- In request handlers, treat a missing bound as 'export all' explicitly rather than forwarding partial bounds.
- Add a unit test covering (start-only), (end-only), (both), (neither).
When it happens
Trigger: Calling export_usage_data(start_time_utc=ts) or export_usage_data(end_time_utc=ts) with the other bound omitted — e.g. a caller forwarding optional query params from an HTTP endpoint where only one was supplied.
Common situations: REST handler mapping optional start/end query params straight into the call; a client sending only 'since'; datetime parsed as falsy on one side (note: datetime.min-like values are fine, but None defaults trip it).
Related errors
- 'models' param not in kwargs
- bucket_name must be provided for S3 destination
- Export format '{self.export_format}' not supported. Use 'par
- Unsupported frequency: {self.frequency}
- start_time_utc is required for getting a payload from GCS Bu
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/0475238207ccb2c5.
Report an issue: GitHub.