{"record":{"id":"0f1bd850f11d9ad2","repo":"BerriAI/litellm","slug":"microsoft-purview-dlp-responses-api-input-could-n","errorCode":null,"errorMessage":"Microsoft Purview DLP: Responses API input could not be transformed for DLP scanning in blocking mode","messagePattern":"Microsoft Purview DLP: Responses API input could not be transformed for DLP scanning in blocking mode","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/purview_dlp.py","lineNumber":324,"sourceCode":"\n        input_data: Final = data.get(\"input\")\n        if input_data is None and not data.get(\"instructions\"):\n            return None\n        try:\n            # Always transform via messages so ``instructions`` become a system message\n            # (string ``input`` alone would skip instructions and bypass DLP).\n            messages: Final = LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages(\n                input=input_data if input_data is not None else \"\",\n                responses_api_request=data,\n            )\n            return self.get_prompt_text_for_dlp(cast(list[Any], messages))\n        except Exception:\n            verbose_proxy_logger.warning(\n                \"Purview DLP: failed to transform responses API input\",\n                exc_info=True,\n            )\n            if raise_on_failure:\n                raise HTTPException(\n                    status_code=400,\n                    detail={\n                        \"error\": (\n                            \"Microsoft Purview DLP: Responses API input could \"\n                            \"not be transformed for DLP scanning in blocking mode\"\n                        ),\n                    },\n                )\n            return None\n\n    # ------------------------------------------------------------------\n    # Identity resolution for blocking modes\n    # ------------------------------------------------------------------\n\n    def _resolve_user_id_for_blocking(\n        self,\n        data: dict[str, Any],\n        user_api_key_dict: Any,","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/purview_dlp.py#L306-L342","documentation":"Fail-closed HTTPException from the Purview pre-call hook for /v1/responses requests. Before scanning, the guardrail converts the Responses API 'input' into chat messages via LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages; if that transform raises for the given input shape, blocking mode cannot guarantee coverage, so the request is rejected with 400 rather than silently bypassing DLP.","triggerScenarios":"Calling /v1/responses through the proxy with an unusual or malformed items array in 'input' (unsupported item/typed-content types), with guardrail mode including pre_call and blocking enabled; None input where the fallback empty-string path also fails; a litellm version whose transformer does not yet support a newer Responses API input type","commonSituations":"Adopting the Responses API alongside a recently added Purview guardrail; sending function/tool outputs or nested typed content blocks the transformer cannot flatten; upgrading one side (proxy vs client SDK) so accepted input shapes drift","solutions":["Simplify the Responses API input: use a plain string or the standard, well-supported items shapes and retest","Upgrade litellm to the latest patch so the responses->messages transformer covers more input types","If it persists, capture the logged traceback ('Purview DLP: failed to transform responses API input' warning with exc_info) and file an issue with a minimal input payload","As a workaround for non-sensitive traffic, route those calls to a deployment without the Purview guardrail attached — do not disable blocking globally"],"exampleFix":"# before: exotic input shape\nresp = client.responses.create(\n    model=\"gpt-4o\",\n    input=[{\"type\":\"item_reference\", \"id\": \"item_abc\"}],\n)\n\n# after: plain well-formed input the DLP scan can transform\nresp = client.responses.create(\n    model=\"gpt-4o\",\n    input=[{\"role\": \"user\", \"content\": \"Summarize the quarterly report\"}],\n)","handlingStrategy":"validation","validationCode":"# Client-side: sanity-check Responses API input shape before sending through guarded routes\nSUPPORTED_ITEM_TYPES = {\"message\", \"function_call\", \"function_call_output\", \"reasoning\"}\n\ndef responses_input_scannable(inp) -> bool:\n    if isinstance(inp, str):\n        return True\n    if isinstance(inp, list):\n        return all(\n            isinstance(i, dict) and i.get(\"type\") in SUPPORTED_ITEM_TYPES\n            for i in inp\n        )\n    return False","typeGuard":"def is_plain_responses_input(x: object) -> bool:\n    \"\"\"True for string or standard item shapes the DLP transform handles.\"\"\"\n    if isinstance(x, str):\n        return True\n    if isinstance(x, list):\n        return all(isinstance(i, dict) and i.get(\"type\") in {\"message\"} for i in x)\n    return False","tryCatchPattern":"try:\n    resp = client.responses.create(model=m, input=inp)\nexcept BadRequestError as e:\n    if \"could not be transformed for DLP scanning\" in str(e):\n        simplify_input_and_retry(inp)  # fall back to plain string input\n    raise","preventionTips":["Pin the shapes you send to documented Responses API item types; avoid exotic/typed content blocks on guarded routes","Add contract tests that send your real payload shapes through a staging proxy with the guardrail enabled","Keep litellm patched so the responses->messages transformer gains new input types"],"tags":["microsoft-purview","guardrails","responses-api","transformation","fail-closed"],"backgroundTag":"request-transformation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}