BerriAI/litellm · error · ValueError

Mavvrik FOCUS destination: {label} must be HTTPS, got scheme

Error message

Mavvrik FOCUS destination: {label} must be HTTPS, got scheme '{parsed.scheme}'

What it means

_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.

Source

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

# GCS requires intermediate chunks to be a multiple of 256 KB.
# 8 MB gives a good balance between round-trips and memory pressure.
_GCS_CHUNK_SIZE: Final = 8 * 1024 * 1024  # 8 MB


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")

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Report the incident to Mavvrik — their API returned a non-https upload URL.
  2. Check for intercepting proxies (corporate MITM, service mesh) that may rewrite response bodies.
  3. Upgrade LiteLLM/mavvrik integration in case a URL-normalization fix shipped.
  4. Retry the export once; transient API misconfigurations are usually short-lived.
Defensive patterns

Strategy: try-catch

Validate before calling

from urllib.parse import urlparse

def assert_https_upload_url(url: str) -> None:
    if urlparse(url).scheme != "https":
        raise SecurityAlert(f"Mavvrik returned non-https upload URL: {url[:60]}")

Type guard

from urllib.parse import urlparse

def is_https_gcs_url(url: str) -> bool:
    p = urlparse(url)
    return p.scheme == "https" and (p.hostname or "").endswith("storage.googleapis.com")

Try / catch

try:
    await dest.deliver(content=csv, time_window=tw, filename="f.csv")
except (RuntimeError, ValueError) as e:
    if "must be HTTPS" in str(e):
        security_alert("Mavvrik returned a plaintext upload URL — possible MITM")
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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