BerriAI/litellm · error · ValueError

bucket_name must be provided for S3 destination

Error message

bucket_name must be provided for S3 destination

What it means

FocusS3Destination.__init__ raises this ValueError when the destination config dict for a FOCUS billing-data export has no falsy 'bucket_name' key. The S3 destination is normally built by the destination factory, which resolves bucket_name from overrides or the FOCUS_S3_BUCKET_NAME environment variable before constructing the class. So seeing this exact message usually means the class was instantiated directly (or via a config path) without that resolution happening.

Solutions

  1. Set the environment variable FOCUS_S3_BUCKET_NAME to the target bucket (the factory reads it automatically).
  2. Pass bucket_name in the destination config overrides, e.g. destination_config={'bucket_name': 'my-focus-export-bucket'}, or the equivalent YAML field for S3 FOCUS exports.
  3. If constructing FocusS3Destination yourself, always include config['bucket_name'] and fail fast with your own clearer error before calling the constructor.
  4. Verify the value is a non-empty string — empty string is falsy and still triggers the error.

Example fix

# before
FocusS3Destination(prefix='focus/exports', config={'region': 'us-east-1'})

# after
FocusS3Destination(
    prefix='focus/exports',
    config={'bucket_name': 'my-focus-export-bucket', 'region': 'us-east-1'},
)
Defensive patterns

Strategy: validation

Validate before calling

import os

bucket = overrides.get('bucket_name') or os.getenv('FOCUS_S3_BUCKET_NAME')
if not bucket:
    raise SystemExit('FOCUS_S3_BUCKET_NAME (or bucket_name override) is required before starting FOCUS exports')

Type guard

def has_s3_bucket(config: dict) -> bool:
    return bool(config.get('bucket_name') or os.getenv('FOCUS_S3_BUCKET_NAME'))

Try / catch

try:
    dest = FocusS3Destination(prefix=prefix, config=config)
except ValueError as e:
    if 'bucket_name' in str(e):
        raise ConfigError('Set FOCUS_S3_BUCKET_NAME before enabling S3 FOCUS exports') from e
    raise

Prevention

When it happens

Trigger: Constructing FocusS3Destination(prefix=..., config={'bucket_name': ''}) or config with no bucket_name key; instantiating the class directly instead of going through the factory when FOCUS_S3_BUCKET_NAME is unset; passing a destination_config dict from YAML that omits bucket_name.

Common situations: Enabling FOCUS exports to S3 in litellm proxy config while forgetting the bucket setting; typos in the env var name (e.g. S3_BUCKET_NAME instead of FOCUS_S3_BUCKET_NAME); empty string values from templated config files; local dev where the env var is only set in production.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/8dbd3420e405e61f. Report an issue: GitHub.

Appendix: source

Thrown at litellm/integrations/focus/destinations/s3_destination.py:26

import boto3

from .base import FocusDestination, FocusTimeWindow


class FocusS3Destination(FocusDestination):
    """Handles uploading serialized exports to S3 buckets."""

    def __init__(
        self,
        *,
        prefix: str,
        config: dict[str, Any] | None = None,
    ) -> None:
        config = config or {}
        bucket_name: Final = config.get("bucket_name")
        if not bucket_name:
            raise ValueError("bucket_name must be provided for S3 destination")
        self.bucket_name = bucket_name
        self.prefix = prefix.rstrip("/")
        self.config = config

    async def deliver(
        self,
        *,
        content: bytes,
        time_window: FocusTimeWindow,
        filename: str,
    ) -> None:
        object_key: Final = self._build_object_key(time_window=time_window, filename=filename)
        await asyncio.to_thread(self._upload, content, object_key)

    def _build_object_key(self, *, time_window: FocusTimeWindow, filename: str) -> str:
        start_utc: Final = time_window.start_time.astimezone(timezone.utc)
        date_component: Final = f"date={start_utc.strftime('%Y-%m-%d')}"
        parts: Final = [self.prefix, date_component]

View on GitHub (pinned to 77b7c6c40c)