BerriAI/litellm · error · RuntimeError

Mavvrik FOCUS destination: GCS session init missing Location

Error message

Mavvrik FOCUS destination: GCS session init missing Location header

What it means

A successful GCS resumable-upload session init (200/201) must return the session URI in the Location response header. If the header is absent or empty, the destination cannot proceed with chunked upload and raises. The session URI is subsequently validated to be an https GCS endpoint (errors 409/410).

Source

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

        metadata: Final = b'{"contentEncoding":"gzip","contentDisposition":"attachment"}'
        init_resp: Final = await self._http.client.request(
            method="POST",
            url=signed_url,
            headers={
                "Content-Type": "application/gzip",
                "x-goog-resumable": "start",
            },
            content=metadata,
            timeout=30.0,
        )
        if init_resp.status_code not in (200, 201):
            raise RuntimeError(
                f"Mavvrik FOCUS destination: GCS session init failed ({init_resp.status_code}): {init_resp.text[:400]}"
            )

        session_uri: Final = init_resp.headers.get("Location")
        if not session_uri:
            raise RuntimeError("Mavvrik FOCUS destination: GCS session init missing Location header")
        _validate_gcs_url(session_uri, "session URI")

        verbose_logger.debug(
            "Mavvrik FOCUS destination: GCS session started, uploading %d gzip bytes in %d chunk(s)",
            total,
            max(1, -(-total // _GCS_CHUNK_SIZE)),  # ceiling division
        )

        # Step 2: upload in chunks; cancel session on any failure to avoid
        # lingering GCS sessions (they stay open for ~1 week otherwise).
        offset = 0
        try:
            while offset < total:
                chunk = gzip_bytes[offset : offset + _GCS_CHUNK_SIZE]
                chunk_end = offset + len(chunk) - 1
                is_final = (offset + len(chunk)) >= total
                content_range = f"bytes {offset}-{chunk_end}/{total}" if is_final else f"bytes {offset}-{chunk_end}/*"
                expected_statuses = {200, 201} if is_final else {308}

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Check for intercepting proxies on the egress path and ensure the Location header is passed through unmodified.
  2. Retry the delivery — a fresh signed URL and session init may succeed.
  3. Verify the client hits storage.googleapis.com directly (no transparent rewriting) by dumping response headers in a manual test.
  4. Report to LiteLLM/Mavvrik if it persists with no proxy in the path.
Defensive patterns

Strategy: retry

Try / catch

try:
    await mavvrik_dest.deliver(content=csv, time_window=tw, filename="f.csv")
except RuntimeError as e:
    if "missing Location header" in str(e):
        # likely a header-stripping proxy or transient GCS anomaly; a new session usually works
        await asyncio.sleep(5)
        await mavvrik_dest.deliver(content=csv, time_window=tw, filename="f.csv")
    else:
        raise

Prevention

When it happens

Trigger: GCS (or an intermediary) returns 200 to the x-goog-resumable start POST but omits the Location header — e.g. a proxy stripping redirect/Location headers, or an API-gateway response that doesn't forward GCS headers.

Common situations: Corporate proxies or service meshes (Envoy/ISTIO with header rewriting) stripping Location; rarely a GCS API anomaly. Pure direct-to-storage.googleapis.com traffic virtually never hits this.

Related errors


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