BerriAI/litellm · error · ValueError

Mavvrik FOCUS destination: {label} must be a GCS endpoint (s

Error message

Mavvrik FOCUS destination: {label} must be a GCS endpoint (storage.googleapis.com), got '{hostname}'

What it means

The second _validate_gcs_url() check requires the hostname of a Mavvrik-provided URL to be exactly storage.googleapis.com or a subdomain ending in .storage.googleapis.com. Any other host (e.g. a different cloud provider, an attacker domain, or a CNAME alias) is rejected, preventing the gzip'd FOCUS data from being exfiltrated to a third party.

Source

Thrown at litellm/integrations/focus/destinations/mavvrik_destination.py:47


def _validate_api_endpoint(api_endpoint: str) -> None:
    if not api_endpoint.startswith("https://"):
        raise ValueError("MAVVRIK_API_ENDPOINT must be an HTTPS URL")
    hostname: Final = (urlparse(api_endpoint).hostname or "").lower()
    if not any(hostname.endswith(suffix) for suffix in _MAVVRIK_ALLOWED_SUFFIXES):
        raise ValueError(
            "MAVVRIK_API_ENDPOINT host must be a Mavvrik domain (e.g. https://api.mavvrik.dev/<tenant_id>)"
        )


def _validate_gcs_url(url: str, label: str) -> None:
    parsed: Final = urlparse(url)
    if parsed.scheme != "https":
        raise ValueError(f"Mavvrik FOCUS destination: {label} must be HTTPS, got scheme '{parsed.scheme}'")
    hostname: Final = (parsed.hostname or "").lower()
    if not (hostname == "storage.googleapis.com" or hostname.endswith(".storage.googleapis.com")):
        raise ValueError(
            f"Mavvrik FOCUS destination: {label} must be a GCS endpoint (storage.googleapis.com), got '{hostname}'"
        )


class FocusMavvrikDestination(FocusDestination):
    """Upload FOCUS CSV exports to Mavvrik via GCS signed URL."""

    def __init__(
        self,
        *,
        prefix: str,
        config: dict[str, Any] | None = None,
    ) -> None:
        config = config or {}
        api_key: Final = config.get("api_key")
        api_endpoint: Final = config.get("api_endpoint")
        connection_id: Final = config.get("connection_id")

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Verify your MAVVRIK_API_ENDPOINT is an official domain and the tenant/connection_id are correct (misrouted tenants can return odd URLs).
  2. Report to Mavvrik support if the URL legitimately should be a proxy domain — the allow-list only accepts storage.googleapis.com.
  3. Upgrade LiteLLM in case the allow-list was updated for new Mavvrik infrastructure.
  4. Treat repeated occurrences as a potential security event and audit what host was returned.
Defensive patterns

Strategy: try-catch

Validate before calling

from urllib.parse import urlparse

def assert_gcs_host(url: str, label: str) -> None:
    host = (urlparse(url).hostname or "").lower()
    if host != "storage.googleapis.com" and not host.endswith(".storage.googleapis.com"):
        raise SecurityAlert(f"{label} points at non-GCS host '{host}' — refusing upload")

Try / catch

try:
    await dest.deliver(content=csv, time_window=tw, filename="f.csv")
except (RuntimeError, ValueError) as e:
    if "must be a GCS endpoint" in str(e):
        security_alert("Mavvrik redirecting FOCUS exports off Google Cloud Storage")
    raise

Prevention

When it happens

Trigger: The signed URL or resumable-session Location returned by the Mavvrik API points to a non-GCS host, e.g. https://uploads.mavvrik.dev/signed or an S3 URL; validation fires inside _get_signed_url or after session init.

Common situations: Mavvrik migrating their upload infrastructure to a proxy/CNAME not under storage.googleapis.com; a misconfigured or compromised Mavvrik tenant; API response shape change putting a dashboard URL in the 'url' field.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/72a10bd1392bd14a. Report an issue: GitHub.