BerriAI/litellm · error · ValueError

Azure Document Intelligence returned 202 but no Operation-Lo

Error message

Azure Document Intelligence returned 202 but no Operation-Location header found

What it means

Raised when the initial Azure DI submit returns HTTP 202 but the Operation-Location response header is absent, leaving LiteLLM no URL to poll for the async analysis result. Azure DI always includes this header on an accepted analyze request, so its absence indicates a protocol break — a proxy stripping headers, a wrong endpoint that returns 202 from something else, or an API-version mismatch.

Source

Thrown at litellm/llms/azure_ai/ocr/document_intelligence/transformation.py:584

            self._check_timeout(start_time=start_time, timeout_secs=timeout_secs)

            # Poll the operation status
            response = await client.get(url=operation_url, headers=headers)

            # Check operation status
            status = self._check_operation_status(response=response)

            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.

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Ensure api_base/AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT points directly at your Document Intelligence resource and the model is a valid prebuilt/custom model ID.
  2. Check intermediaries (proxy/gateway) for header stripping and whitelist Operation-Location.
  3. Retry once — transient Azure anomalies do occur; if persistent, capture headers with curl to confirm what's returned.
Defensive patterns

Strategy: validation

Try / catch

try:
    resp = litellm.aocr_document(model=m, document=doc)
except ValueError as e:
    if "no Operation-Location header" in str(e):
        raise InfrastructureError("DI endpoint/proxy dropped Operation-Location; check api_base and gateways") from e
    raise

Prevention

When it happens

Trigger: An intermediary (corporate proxy, API gateway, custom base URL) returning its own 202 without forwarding Azure's Operation-Location; api_base pointing at a non-DI endpoint that happens to return 202; Azure API version changes altering response headers.

Common situations: Self-hosted gateways or mTLS terminators dropping unknown headers; misconfigured api_base; testing with mocks that don't emulate the header; rare Azure-side incidents.

Related errors


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