{"record":{"id":"10566a36bc5ac009","repo":"BerriAI/litellm","slug":"mavvrik-focus-destination-label-must-be-https","errorCode":null,"errorMessage":"Mavvrik FOCUS destination: {label} must be HTTPS, got scheme '{parsed.scheme}'","messagePattern":"Mavvrik FOCUS destination: (.+?) must be HTTPS, got scheme '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/integrations/focus/destinations/mavvrik_destination.py","lineNumber":44,"sourceCode":"# GCS requires intermediate chunks to be a multiple of 256 KB.\n# 8 MB gives a good balance between round-trips and memory pressure.\n_GCS_CHUNK_SIZE: Final = 8 * 1024 * 1024  # 8 MB\n\n\ndef _validate_api_endpoint(api_endpoint: str) -> None:\n    if not api_endpoint.startswith(\"https://\"):\n        raise ValueError(\"MAVVRIK_API_ENDPOINT must be an HTTPS URL\")\n    hostname: Final = (urlparse(api_endpoint).hostname or \"\").lower()\n    if not any(hostname.endswith(suffix) for suffix in _MAVVRIK_ALLOWED_SUFFIXES):\n        raise ValueError(\n            \"MAVVRIK_API_ENDPOINT host must be a Mavvrik domain (e.g. https://api.mavvrik.dev/<tenant_id>)\"\n        )\n\n\ndef _validate_gcs_url(url: str, label: str) -> None:\n    parsed: Final = urlparse(url)\n    if parsed.scheme != \"https\":\n        raise ValueError(f\"Mavvrik FOCUS destination: {label} must be HTTPS, got scheme '{parsed.scheme}'\")\n    hostname: Final = (parsed.hostname or \"\").lower()\n    if not (hostname == \"storage.googleapis.com\" or hostname.endswith(\".storage.googleapis.com\")):\n        raise ValueError(\n            f\"Mavvrik FOCUS destination: {label} must be a GCS endpoint (storage.googleapis.com), got '{hostname}'\"\n        )\n\n\nclass FocusMavvrikDestination(FocusDestination):\n    \"\"\"Upload FOCUS CSV exports to Mavvrik via GCS signed URL.\"\"\"\n\n    def __init__(\n        self,\n        *,\n        prefix: str,\n        config: dict[str, Any] | None = None,\n    ) -> None:\n        config = config or {}\n        api_key: Final = config.get(\"api_key\")","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/focus/destinations/mavvrik_destination.py#L26-L62","documentation":"_validate_gcs_url() is applied to URLs the Mavvrik API hands back (the signed upload URL and the resumable-session URI). It first requires scheme https; anything else raises with the offending scheme interpolated. This guards against a compromised or misbehaving server redirecting credential-bearing uploads to plaintext.","triggerScenarios":"The Mavvrik upload-url endpoint returns a signed URL like http://storage.googleapis.com/... (scheme http, gs:// etc.), and the destination then calls _get_signed_url or validates the session Location header.","commonSituations":"A Mavvrik API change or bug returning http URLs; a man-in-the-middle/proxy rewriting https to http; extremely rare in normal operation since GCS signed URLs are https — seeing this usually means the response was tampered with or the endpoint returned an error page whose 'url' field points elsewhere.","solutions":["Report the incident to Mavvrik — their API returned a non-https upload URL.","Check for intercepting proxies (corporate MITM, service mesh) that may rewrite response bodies.","Upgrade LiteLLM/mavvrik integration in case a URL-normalization fix shipped.","Retry the export once; transient API misconfigurations are usually short-lived."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"from urllib.parse import urlparse\n\ndef assert_https_upload_url(url: str) -> None:\n    if urlparse(url).scheme != \"https\":\n        raise SecurityAlert(f\"Mavvrik returned non-https upload URL: {url[:60]}\")","typeGuard":"from urllib.parse import urlparse\n\ndef is_https_gcs_url(url: str) -> bool:\n    p = urlparse(url)\n    return p.scheme == \"https\" and (p.hostname or \"\").endswith(\"storage.googleapis.com\")","tryCatchPattern":"try:\n    await dest.deliver(content=csv, time_window=tw, filename=\"f.csv\")\nexcept (RuntimeError, ValueError) as e:\n    if \"must be HTTPS\" in str(e):\n        security_alert(\"Mavvrik returned a plaintext upload URL — possible MITM\")\n    raise","preventionTips":["Monitor for this error: a non-https signed URL from a trusted API is a security signal, not a flake.","Keep egress TLS-terminating proxies from rewriting response bodies on the storage.googleapis.com path."],"tags":["focus","mavvrik","gcs","security","validation","network"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}