{"record":{"id":"87c8cba0c32a0e12","repo":"BerriAI/litellm","slug":"gcs-upload-failed-status-response-status-code-b","errorCode":null,"errorMessage":"GCS upload failed: status={response.status_code} body={response.text}","messagePattern":"GCS upload failed: status=(.+?) body=(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"litellm/integrations/focus/destinations/gcs_destination.py","lineNumber":53,"sourceCode":"\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 = (\n            f\"https://storage.googleapis.com/upload/storage/v1/b/\"\n            f\"{self.BUCKET_NAME}/o?uploadType=media&name={encoded_name}\"\n        )\n        response: Final = await self.async_httpx_client.post(url=url, headers=headers, data=content)\n        if response.status_code != 200:\n            raise RuntimeError(f\"GCS upload failed: status={response.status_code} body={response.text}\")\n        verbose_logger.debug(\n            \"Focus GCS: uploaded %d bytes to gs://%s/%s\",\n            len(content),\n            self.BUCKET_NAME,\n            object_name,\n        )\n\n    def _build_object_key(self, *, time_window: FocusTimeWindow, filename: str) -> str:\n        start_utc: Final = time_window.start_time.astimezone(timezone.utc)\n        date_component: Final = f\"date={start_utc.strftime('%Y-%m-%d')}\"\n        parts: Final = [self.prefix, date_component]\n        if time_window.frequency == \"hourly\":\n            parts.append(f\"hour={start_utc.strftime('%H')}\")\n        key_prefix: Final = \"/\".join(filter(None, parts))\n        return f\"{key_prefix}/{filename}\" if key_prefix else filename\n","sourceCodeStart":35,"sourceCodeEnd":69,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/focus/destinations/gcs_destination.py#L35-L69","documentation":"After FocusGCSDestination.deliver() POSTs the export bytes to the GCS JSON API upload endpoint (uploadType=media), any response other than HTTP 200 raises RuntimeError embedding the status code and response body. Because the message is an f-string, the actual error contains the concrete status/body — common causes are 401/403 auth failures, 404 wrong bucket, and 400 malformed object name.","triggerScenarios":"Calling deliver() on a FocusGCSDestination when the service-account/default credentials lack storage.objects.create on the bucket (403), the bucket name is wrong or deleted (404), the OAuth token fetch failed (401), or the URL-encoded object name is rejected (400).","commonSituations":"Workload Identity not bound to the service account; service account JSON expired or for a different project; bucket deleted or renamed after config; VPC-SC perimeter blocking storage.googleapis.com; regional bucket access restrictions.","solutions":["Read the embedded status/body: 401/403 means credentials — verify FOCUS_GCS_PATH_SERVICE_ACCOUNT or ambient ADC, and grant roles/storage.objectCreator on the bucket.","404 means bucket mismatch — confirm the bucket_name used at construction matches an existing bucket in that project.","Test auth independently: gcloud auth application-default print-access-token and a manual upload to the bucket.","For network/VPC-SC errors (7xx/403 with 'Request is prohibited'), check egress rules and service perimeter configuration.","Retry once on transient 5xx — GCS occasionally returns 500/503."],"exampleFix":"# before\nawait destination.deliver(content=csv_bytes, time_window=tw, filename=\"focus.csv\")\n\n# after\ntry:\n    await destination.deliver(content=csv_bytes, time_window=tw, filename=\"focus.csv\")\nexcept RuntimeError as e:\n    log.error(\"GCS export failed: %s\", e)  # message includes status + body\n    raise","handlingStrategy":"retry","validationCode":"# Pre-flight: bucket exists and caller has objectCreator before first export\nimport subprocess\nsubprocess.run([\"gcloud\", \"storage\", \"buckets\", \"describe\", f\"gs://{bucket_name}\"], check=True)","typeGuard":null,"tryCatchPattern":"import re\n\nasync def safe_deliver(dest, **kw):\n    try:\n        return await dest.deliver(**kw)\n    except RuntimeError as e:\n        msg = str(e)\n        if re.search(r\"status=(5\\d\\d|429)\", msg):\n            await asyncio.sleep(5)\n            return await dest.deliver(**kw)  # one retry on transient\n        raise  # 401/403/404 are config issues — surface them","preventionTips":["Grant roles/storage.objectCreator to the exporting identity and verify with a test upload at deploy time.","Alert on the embedded status code: repeated 403s indicate drift between configured SA and bucket IAM.","Keep exports idempotent (same object key per window) so retries are safe."],"tags":["focus","gcs","network","upload","permissions"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}