BerriAI/litellm · error · RuntimeError
Mavvrik FOCUS destination: response missing 'url' field: {re
Error message
Mavvrik FOCUS destination: response missing 'url' field: {resp.json()} What it means
The upload-url endpoint returned 2xx but its JSON body has no 'url' field (or it's null/empty). This indicates a Mavvrik API contract change or an unexpected (e.g. HTML) response that happened to return 200 — the code dumps resp.json() into the message for diagnosis.
Source
Thrown at litellm/integrations/focus/destinations/mavvrik_destination.py:153
return metrics_marker
async def _get_signed_url(self, date_str: str) -> str:
"""GET upload-url endpoint → GCS signed URL for the given date."""
params: Final = {"name": date_str, "type": "metrics", "datetime": date_str}
resp: Final = await self._http.client.request(
method="GET",
url=self._upload_url_endpoint,
headers=self._auth_headers,
params=params,
timeout=30.0,
)
if resp.status_code >= 400:
raise RuntimeError(
f"Mavvrik FOCUS destination: failed to get signed URL ({resp.status_code}): {resp.text[:200]}"
)
signed_url: Final = resp.json().get("url")
if not signed_url:
raise RuntimeError(f"Mavvrik FOCUS destination: response missing 'url' field: {resp.json()}")
_validate_gcs_url(signed_url, "signed URL")
verbose_logger.debug("Mavvrik FOCUS destination: got signed URL for date %s", date_str)
return signed_url
async def _upload_to_gcs(self, signed_url: str, content: bytes) -> None:
"""Upload gzip-compressed CSV to GCS via chunked resumable upload.
The full CSV is gzip-compressed first, then uploaded in _GCS_CHUNK_SIZE
chunks using the GCS resumable upload protocol. GCS assembles the chunks
server-side into a single complete object — the bucket receives one file
regardless of how many chunks were sent.
Intermediate chunks: Content-Range: bytes X-Y/* → expect 308
Final chunk: Content-Range: bytes X-Y/T → expect 200/201
This handles exports larger than available memory for a single PUT while
keeping the destination code self-contained (no changes to the FOCUS
pipeline upstream).View on GitHub (pinned to 6c2dcb801b)
Solutions
- Read the embedded body in the message to see what fields were actually returned.
- Upgrade LiteLLM to the latest version — response-shape fixes are usually shipped quickly after API changes.
- If the field was renamed on Mavvrik's side, report it / pin a compatible Mavvrik API version if available.
- Retry once to rule out a transient gateway glitch.
Defensive patterns
Strategy: try-catch
Try / catch
try:
await mavvrik_dest.deliver(content=csv, time_window=tw, filename="f.csv")
except RuntimeError as e:
if "missing 'url' field" in str(e):
log.error("Mavvrik API contract drift: %s", e)
# one retry in case of gateway glitch, then open a support ticket
raise Prevention
- Pin a LiteLLM version tested against your Mavvrik tenant; upgrade deliberately when APIs change.
- Log the full response body (already embedded in the message) into your error tracker for quick diagnosis.
When it happens
Trigger: Calling _get_signed_url when the Mavvrik API responds 200 with a payload like {"signedUrl": ...} (renamed field), an empty object, or a JSON error envelope without 'url'.
Common situations: Mavvrik API version change renaming the field; a gateway returning 200 with an error body; proxy health-check intercepting the path; older/newer LiteLLM expecting a different response shape.
Related errors
- Mavvrik FOCUS destination: {label} must be HTTPS, got scheme
- Mavvrik FOCUS destination: register failed ({resp.status_cod
- Mavvrik FOCUS destination: failed to get signed URL ({resp.s
- Mavvrik FOCUS destination: GCS session init failed ({init_re
- Mavvrik FOCUS destination: GCS session init missing Location
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/0f0a8c6c430731f3.
Report an issue: GitHub.