BerriAI/litellm · error · ValueError
Azure Document Intelligence: rejected polling URL ({ssrf_err
Error message
Azure Document Intelligence: rejected polling URL ({ssrf_err}) What it means
Security guard (VERIA-51): the Operation-Location header returned by Azure must be same-origin with the original request URL, otherwise LiteLLM rejects the polling URL. Without this check, the Ocp-Apim-Subscription-Key header would be sent to whatever host appears in the attacker-controlled (or misconfigured-upstream) redirect, leaking the subscription key. An SSRFError from assert_same_origin is wrapped in this ValueError.
Source
Thrown at litellm/llms/azure_ai/ocr/document_intelligence/transformation.py:592
if status == "succeeded":
return response
elif status == "running":
# Wait before polling again
retry_after = self._get_retry_after(response=response)
await asyncio.sleep(retry_after)
def _get_polling_target(self, raw_response: httpx.Response) -> tuple[str, dict[str, str]]:
operation_url: Final = raw_response.headers.get("Operation-Location")
if not operation_url:
raise ValueError("Azure Document Intelligence returned 202 but no Operation-Location header found")
# Reject cross-origin polling URLs — the auth headers
# below would otherwise leak to whatever URL the upstream
# (or an attacker-controlled upstream) returns. VERIA-51.
try:
assert_same_origin(operation_url, str(raw_response.request.url))
except SSRFError as ssrf_err:
raise ValueError(f"Azure Document Intelligence: rejected polling URL ({ssrf_err})")
poll_headers = {"Ocp-Apim-Subscription-Key": raw_response.request.headers.get("Ocp-Apim-Subscription-Key", "")}
return operation_url, poll_headers
def _transform_completed_response(self, model: str, raw_response: httpx.Response) -> OCRResponse:
"""
Transform a completed Azure Document Intelligence analyze operation
into the Mistral OCR response shape, preserving Azure-native
`analyzeResult` fields (`content`, `tables`, `keyValuePairs`) as
top-level response fields.
"""
operation: Final = AzureDocumentIntelligenceOperation.model_validate(raw_response.json())
verbose_logger.debug("Azure Document Intelligence response status: %s", operation.status)
if operation.status != "succeeded":
raise ValueError(f"Azure Document Intelligence analysis failed with status: {operation.status}")
View on GitHub (pinned to 6c2dcb801b)
Solutions
- Make the Operation-Location your endpoint returns use the exact same origin (scheme+host+port) as the analyze request URL — essential for mock servers.
- Point api_base directly at the real Azure resource so Azure's own same-origin header passes the check.
- Audit any intermediary that rewrites Location/Operation-Location headers and disable that rewrite.
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def polling_url_is_safe(analyze_url: str, operation_url: str) -> bool:
a, b = urlparse(analyze_url), urlparse(operation_url)
return (a.scheme, a.hostname, a.port) == (b.scheme, b.hostname, b.port) Try / catch
try:
resp = litellm.aocr_document(model=m, document=doc)
except ValueError as e:
if "rejected polling URL" in str(e):
raise SecurityError("cross-origin Operation-Location — check proxy/mock config") from e
raise Prevention
- Mock DI servers must return Operation-Location on the same origin as the request.
- Never let user-supplied api_base go unvalidated; this guard exists to stop key leakage.
- Disable header-rewriting on any intermediary in front of the DI endpoint.
When it happens
Trigger: A DNS rebinding or header-injection attack making Operation-Location point at a foreign host; a misconfigured proxy rewriting the header to a different origin; pointing api_base at a mock/test server that echoes a cross-origin operation URL. Any host/port/scheme mismatch versus the original request URL triggers it.
Common situations: Local development with a mock DI server that returns absolute URLs on another port; a gateway that rewrites Location-family headers; genuinely malicious upstreams when api_base is user-controlled.
Related errors
- Azure Document Intelligence returned 202 but no Operation-Lo
- Polling response missing 'status' field
- Azure Document Intelligence operation polling timed out afte
- Azure Document Intelligence analysis failed: {error_msg}
- Unknown operation status: {status}
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/a20ae2cabb618889.
Report an issue: GitHub.