{"record":{"id":"970276fd0e904237","repo":"BerriAI/litellm","slug":"bucket-name-must-be-provided-for-gcs-destination","errorCode":null,"errorMessage":"bucket_name must be provided for GCS destination","messagePattern":"bucket_name must be provided for GCS destination","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/integrations/focus/destinations/gcs_destination.py","lineNumber":29,"sourceCode":"    encode_gcs_object_name_for_url,\n)\n\nfrom .base import FocusDestination, FocusTimeWindow\n\n\nclass FocusGCSDestination(GCSBucketBase, FocusDestination):\n    \"\"\"Upload serialized Focus exports to GCS using the GCS JSON API.\"\"\"\n\n    def __init__(\n        self,\n        *,\n        prefix: str,\n        config: dict[str, Any] | None = None,\n    ) -> None:\n        config = config or {}\n        bucket_name: Final = config.get(\"bucket_name\")\n        if not bucket_name:\n            raise ValueError(\"bucket_name must be provided for GCS destination\")\n        super().__init__(bucket_name=bucket_name)\n        service_account_json: Final = config.get(\"service_account_json\")\n        if service_account_json is not None:\n            self.path_service_account_json = service_account_json\n        self.prefix = prefix.rstrip(\"/\")\n\n    async def deliver(\n        self,\n        *,\n        content: bytes,\n        time_window: FocusTimeWindow,\n        filename: str,\n    ) -> None:\n        object_name: Final = self._build_object_key(time_window=time_window, filename=filename)\n        headers: Final = await self.construct_request_headers(service_account_json=self.path_service_account_json)\n        headers[\"Content-Type\"] = \"application/octet-stream\"\n        encoded_name: Final = encode_gcs_object_name_for_url(object_name)\n        url: Final = (","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/focus/destinations/gcs_destination.py#L11-L47","documentation":"FocusGCSDestination.__init__ raises this ValueError when the resolved config dict has no truthy 'bucket_name' key. The destination uploads serialized FOCUS billing exports to Google Cloud Storage, so a target bucket is mandatory; the check fails fast at construction instead of at first upload. Note that when the destination is built through FocusDestinationFactory, the factory raises its own 'FOCUS_GCS_BUCKET_NAME must be provided for GCS exports' first, so this exact message fires when FocusGCSDestination is instantiated directly.","triggerScenarios":"Calling FocusGCSDestination(prefix=..., config=...) with config=None, {}, or a dict where 'bucket_name' is missing, empty string, or None. Via the normal path, setting neither destination_config['bucket_name'] nor the FOCUS_GCS_BUCKET_NAME env var causes the factory's equivalent error before this line is reached.","commonSituations":"FOCUS_GCS_BUCKET_NAME not set in the deployment env, a typo'd config key (e.g. 'bucket' or 'gs://my-bucket' under a different key), YAML/env config where None-valued keys were filtered out before reaching the constructor, or direct instantiation in tests without a config.","solutions":["Pass bucket_name in the destination config: FocusGCSDestination(prefix='focus', config={'bucket_name': 'my-bucket', 'service_account_json': '/path/to/sa.json'})","Or set export FOCUS_GCS_BUCKET_NAME=my-bucket and construct via FocusDestinationFactory.create(provider='gcs', prefix='focus')","Verify the bucket name is exact: no gs:// prefix, no trailing slash, correct project (gcloud storage buckets list)","Also pass service_account_json (or FOCUS_GCS_PATH_SERVICE_ACCOUNT) if you do not rely on Application Default Credentials"],"exampleFix":"# before\nFocusGCSDestination(prefix='focus', config={'bucket': 'litellm-focus'})\n\n# after\nFocusGCSDestination(prefix='focus', config={'bucket_name': 'litellm-focus'})","handlingStrategy":"validation","validationCode":"import os\n\nbucket = (destination_config or {}).get(\"bucket_name\") or os.getenv(\"FOCUS_GCS_BUCKET_NAME\")\nif not bucket:\n    raise SystemExit(\"Set FOCUS_GCS_BUCKET_NAME or destination_config['bucket_name'] before enabling the GCS FOCUS destination\")","typeGuard":"from typing import Any\nimport os\n\ndef has_valid_gcs_bucket(config: dict[str, Any] | None) -> bool:\n    \"\"\"True when a non-empty GCS bucket is resolvable from config or env.\"\"\"\n    return bool((config or {}).get(\"bucket_name\") or os.getenv(\"FOCUS_GCS_BUCKET_NAME\"))","tryCatchPattern":"try:\n    destination = FocusGCSDestination(prefix=\"focus\", config=cfg)\nexcept ValueError as e:\n    if \"bucket_name\" in str(e):\n        raise SystemExit(f\"FOCUS GCS destination misconfigured: {e}\") from e\n    raise","preventionTips":["Fail fast at process start: validate FOCUS_GCS_BUCKET_NAME (or config['bucket_name']) before the export scheduler ever runs","Add the env var to deployment manifests and a config-lint step in CI","Use bare bucket names (no gs:// prefix) everywhere"],"tags":["gcs","focus-export","config","litellm","validation"],"backgroundTag":"missing-required-config","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}