{"record":{"id":"f1fc46cc09e58dd2","repo":"BerriAI/litellm","slug":"microsoft-purview-tenant-id-is-required","errorCode":null,"errorMessage":"Microsoft Purview: tenant_id is required","messagePattern":"Microsoft Purview: tenant_id is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/__init__.py","lineNumber":22,"sourceCode":"\nfrom .purview_dlp import MicrosoftPurviewDLPGuardrail\n\nif TYPE_CHECKING:\n    from litellm.types.guardrails import Guardrail, LitellmParams\n\n\ndef initialize_guardrail(litellm_params: \"LitellmParams\", guardrail: \"Guardrail\"):\n    import litellm\n\n    tenant_id: Final = getattr(litellm_params, \"tenant_id\", None)\n    client_id: Final = getattr(litellm_params, \"client_id\", None)\n\n    # client_secret can be passed via the standard api_key field or as\n    # a dedicated client_secret parameter.\n    client_secret: Final = litellm_params.api_key or getattr(litellm_params, \"client_secret\", None)\n\n    if not tenant_id:\n        raise ValueError(\"Microsoft Purview: tenant_id is required\")\n    if not client_id:\n        raise ValueError(\"Microsoft Purview: client_id is required\")\n    if not client_secret:\n        raise ValueError(\"Microsoft Purview: client_secret (or api_key) is required\")\n\n    guardrail_name: Final = guardrail.get(\"guardrail_name\")\n    if not guardrail_name:\n        raise ValueError(\"Microsoft Purview: guardrail_name is required\")\n\n    purview_guardrail: Final = MicrosoftPurviewDLPGuardrail(\n        guardrail_name=guardrail_name,\n        tenant_id=str(tenant_id),\n        client_id=str(client_id),\n        client_secret=str(client_secret),\n        purview_app_name=str(getattr(litellm_params, \"purview_app_name\", None) or \"LiteLLM\"),\n        user_id_field=str(getattr(litellm_params, \"user_id_field\", None) or \"user_id\"),\n        event_hook=litellm_params.mode,\n        default_on=litellm_params.default_on,","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/__init__.py#L4-L40","documentation":"Config-time ValueError raised by the microsoft_purview guardrail's initialize_guardrail() in the LiteLLM proxy. The Purview DLP integration authenticates to Microsoft Entra ID (client-credentials OAuth), which requires an explicit tenant_id, client_id, and client_secret; there is no environment-variable fallback for tenant_id. The error means the guardrail entry in your proxy config had no 'tenant_id' under litellm_params.","triggerScenarios":"Declaring a guardrail with litellm_params.guardrail: microsoft_purview in config.yaml but omitting tenant_id, or setting it to an empty string/null (the check is falsy-based). Also a key typo such as tenantId or tenant-id, or placing the key at the wrong nesting level (sibling of litellm_params instead of inside it).","commonSituations":"Copying a Purview example config without filling in Azure values; assuming LiteLLM reads AZURE_TENANT_ID from env (it does not); migrating from another Azure guardrail whose config schema put credentials at the top level; hitting this only after adding mode: pre_call which actually instantiates the guardrail at proxy startup.","solutions":["Add 'tenant_id' inside the litellm_params block of that guardrail entry — the Azure tenant GUID shown in Entra ID > App registrations > Overview","While editing, also set client_id and client_secret in the same block so init does not fail on the next check","Restart the LiteLLM proxy so initialize_guardrail re-runs against the fixed config"],"exampleFix":"# before (config.yaml)\nguardrails:\n  - guardrail_name: purview-dlp\n    litellm_params:\n      guardrail: microsoft_purview\n      mode: pre_call\n\n# after\nguardrails:\n  - guardrail_name: purview-dlp\n    litellm_params:\n      guardrail: microsoft_purview\n      mode: pre_call\n      tenant_id: \"11111111-2222-3333-4444-555555555555\"\n      client_id: \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\"\n      client_secret: \"your-app-secret-value\"","handlingStrategy":"validation","validationCode":"# Run before starting the proxy: validate every microsoft_purview guardrail entry\nimport yaml\n\ncfg = yaml.safe_load(open(\"config.yaml\"))\nfor g in cfg.get(\"guardrails\", []):\n    lp = g.get(\"litellm_params\", {})\n    if lp.get(\"guardrail\") == \"microsoft_purview\":\n        missing = [k for k in (\"tenant_id\", \"client_id\", \"client_secret\") if not lp.get(k) and not (k == \"client_secret\" and lp.get(\"api_key\"))]\n        if not g.get(\"guardrail_name\"):\n            missing.insert(0, \"guardrail_name(top-level)\")\n        if missing:\n            raise SystemExit(f\"purview guardrail '{g.get('guardrail_name', '<unnamed>')}' missing: {missing}\")","typeGuard":null,"tryCatchPattern":"# Startup-time: let config errors fail fast and loudly, don't swallow them\ntry:\n    initialize_purview_guardrail_from(cfg)\nexcept ValueError as e:\n    if str(e).startswith(\"Microsoft Purview:\"):\n        log.fatal(\"bad guardrail config: %s\", e)\n        sys.exit(2)\n    raise","preventionTips":["Lint guardrail config in CI: assert required litellm_params keys per guardrail type before deploy","Keep a minimal config template with all Purview fields present (empty) so omissions are obvious","Use env-var interpolation in config.yaml and validate the env vars exist in the deploy pipeline"],"tags":["microsoft-purview","guardrails","configuration","azure-ad","litellm-proxy"],"backgroundTag":"missing-required-config","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}