litellm - ✅(Solved) Fix [Bug]: Azure guardrails (prompt_shield, text_moderations) silently skip on /v1/responses — "No messages in data" [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#25215Fetched 2026-04-08 02:53:01
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
cross-referenced ×1referenced ×1

Root Cause

Both Azure guardrail handlers only look for messages in the request data. When the request comes through /v1/responses, the user content is in the input field instead.

prompt_shield.pyasync_pre_call_hook (line ~133):

new_messages: Optional[List[AllMessageValues]] = data.get("messages")
if new_messages is None:
    verbose_proxy_logger.warning(
        "Azure Prompt Shield: not running guardrail. No messages in data"
    )
    return data

text_moderation.pyasync_pre_call_hook (line ~228):

new_messages: Optional[List[AllMessageValues]] = data.get("messages")
if new_messages is None:
    verbose_proxy_logger.warning(
        "Azure Text Moderation: not running guardrail. No messages in data"
    )
    return data

Both are in:

  • litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py
  • litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py

PR fix notes

PR #25233: fix(guardrails): Azure prompt_shield and text_moderation now check input for Responses API

Description (problem / solution / changelog)

Summary

Fixes Azure guardrails (prompt_shield, text_moderation) silently skipping on /v1/responses endpoint requests.

Root Cause

Both guardrail handlers only check data.get("messages") for user content. The Responses API (/v1/responses) uses data["input"] instead of data["messages"], so the handlers exit early with a warning:

Azure Prompt Shield: not running guardrail. No messages in data
Azure Text Moderation: not running guardrail. No messages in data

This means content moderation and prompt injection detection are completely bypassed for any traffic using the Responses API.

Fix

Added fallback to data.get("input") in both handlers:

# Before
new_messages = data.get("messages")

# After
new_messages = data.get("messages") or data.get("input")

Files Changed

  • litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py
  • litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py

Testing

The fix is a minimal fallback that doesn't change existing /v1/chat/completions behavior (where data["messages"] is always present and truthy). For /v1/responses, data["input"] provides the message list.

Disclaimer

AI agents (Claude Code) assisted with this contribution.

Fixes #25215

Changed files

  • litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py (modified, +14/-0)
  • litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py (modified, +14/-0)
  • tests/test_litellm/proxy/guardrails/guardrail_hooks/azure/test_azure_prompt_shield.py (modified, +120/-0)

Code Example

{"message": "Azure Prompt Shield: not running guardrail. No messages in data", "level": "WARNING"}
{"message": "Azure Text Moderation: not running guardrail. No messages in data", "level": "WARNING"}

---

guardrails:
  - guardrail_name: azure-prompt-shield
    litellm_params:
      guardrail: azure/prompt_shield
      mode: pre_call
      api_key: os.environ/AZURE_GUARDRAIL_API_KEY
      api_base: os.environ/AZURE_GUARDRAIL_API_BASE

  - guardrail_name: azure-text-moderation
    litellm_params:
      guardrail: azure/text_moderations
      mode: pre_call
      api_key: os.environ/AZURE_GUARDRAIL_API_KEY
      api_base: os.environ/AZURE_GUARDRAIL_API_BASE

---

curl -X POST http://localhost:4000/v1/responses \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4.1-mini",
    "input": [{"role": "user", "content": "Some harmful content here"}],
    "guardrails": ["azure-prompt-shield", "azure-text-moderation"]
  }'

---

curl -X POST http://localhost:4000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4.1-mini",
    "messages": [{"role": "user", "content": "Some harmful content here"}],
    "guardrails": ["azure-prompt-shield", "azure-text-moderation"]
  }'

---

new_messages: Optional[List[AllMessageValues]] = data.get("messages")
if new_messages is None:
    verbose_proxy_logger.warning(
        "Azure Prompt Shield: not running guardrail. No messages in data"
    )
    return data

---

new_messages: Optional[List[AllMessageValues]] = data.get("messages")
if new_messages is None:
    verbose_proxy_logger.warning(
        "Azure Text Moderation: not running guardrail. No messages in data"
    )
    return data

---

new_messages = data.get("messages") or data.get("input")
if new_messages is None:
    ...
RAW_BUFFERClick to expand / collapse

What happened?

Azure Content Safety guardrails (azure-prompt-shield and azure-text-moderation) silently skip when requests come through the /v1/responses (OpenAI Responses API) endpoint. The guardrails check data.get("messages") but the Responses API uses input instead, so the handlers exit early with a warning log.

This means content moderation and prompt injection detection are completely bypassed for any traffic using the Responses API, despite PR #15686 adding framework-level guardrail support for this endpoint.

Relevant log output

{"message": "Azure Prompt Shield: not running guardrail. No messages in data", "level": "WARNING"}
{"message": "Azure Text Moderation: not running guardrail. No messages in data", "level": "WARNING"}

How to reproduce

  1. Configure Azure guardrails in litellm_config.yaml:
guardrails:
  - guardrail_name: azure-prompt-shield
    litellm_params:
      guardrail: azure/prompt_shield
      mode: pre_call
      api_key: os.environ/AZURE_GUARDRAIL_API_KEY
      api_base: os.environ/AZURE_GUARDRAIL_API_BASE

  - guardrail_name: azure-text-moderation
    litellm_params:
      guardrail: azure/text_moderations
      mode: pre_call
      api_key: os.environ/AZURE_GUARDRAIL_API_KEY
      api_base: os.environ/AZURE_GUARDRAIL_API_BASE
  1. Send a request to /v1/responses:
curl -X POST http://localhost:4000/v1/responses \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4.1-mini",
    "input": [{"role": "user", "content": "Some harmful content here"}],
    "guardrails": ["azure-prompt-shield", "azure-text-moderation"]
  }'
  1. Observe proxy logs: both guardrails log the warning and exit without scanning.

  2. Send the same content via /v1/chat/completions:

curl -X POST http://localhost:4000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4.1-mini",
    "messages": [{"role": "user", "content": "Some harmful content here"}],
    "guardrails": ["azure-prompt-shield", "azure-text-moderation"]
  }'
  1. Guardrails run correctly via /v1/chat/completions.

Root cause

Both Azure guardrail handlers only look for messages in the request data. When the request comes through /v1/responses, the user content is in the input field instead.

prompt_shield.pyasync_pre_call_hook (line ~133):

new_messages: Optional[List[AllMessageValues]] = data.get("messages")
if new_messages is None:
    verbose_proxy_logger.warning(
        "Azure Prompt Shield: not running guardrail. No messages in data"
    )
    return data

text_moderation.pyasync_pre_call_hook (line ~228):

new_messages: Optional[List[AllMessageValues]] = data.get("messages")
if new_messages is None:
    verbose_proxy_logger.warning(
        "Azure Text Moderation: not running guardrail. No messages in data"
    )
    return data

Both are in:

  • litellm/proxy/guardrails/guardrail_hooks/azure/prompt_shield.py
  • litellm/proxy/guardrails/guardrail_hooks/azure/text_moderation.py

Suggested fix

The async_pre_call_hook in both handlers should fall back to data.get("input") when messages is not present. PR #15686 added get_guardrails_messages_for_call_type() at the framework level — the Azure handlers should use it (or at minimum check input as a fallback):

new_messages = data.get("messages") or data.get("input")
if new_messages is None:
    ...

A more robust approach would be to use the call_type parameter (already passed to async_pre_call_hook) to determine which field to read from, consistent with how PR #15686 handles guardrail message extraction.

The same pattern likely affects async_post_call_success_hook in text_moderation.py which reads response.choices[0].message.content — for Responses API the output structure is different.

LiteLLM version

v1.82.6-nightly (also confirmed on v1.82.3-stable)

Related

  • #14152 — Original bug: Responses API does not support guardrails
  • #15686 — PR that added framework-level guardrail support for /v1/responses and /v1/messages
  • #12268 — PR that added Azure Content Safety guardrails

Are you a LiteLLM customer?

No

Twitter / LinkedIn details

N/A

extent analysis

TL;DR

Update the Azure guardrail handlers to check for both "messages" and "input" fields in the request data to ensure content moderation and prompt injection detection work correctly for the OpenAI Responses API.

Guidance

  • Modify the async_pre_call_hook function in both prompt_shield.py and text_moderation.py to check for the "input" field when "messages" is not present.
  • Consider using the call_type parameter to determine which field to read from, consistent with PR #15686.
  • Review the async_post_call_success_hook in text_moderation.py to handle the different output structure for the Responses API.
  • Test the changes with both /v1/responses and /v1/chat/completions endpoints to ensure guardrails are working correctly.

Example

new_messages = data.get("messages") or data.get("input")
if new_messages is None:
    verbose_proxy_logger.warning(
        "Azure Prompt Shield: not running guardrail. No messages or input in data"
    )
    return data

Notes

The suggested fix assumes that the "input" field has the same structure as the "messages" field. If this is not the case, additional modifications may be needed to handle the different field structures.

Recommendation

Apply the workaround by updating the Azure guardrail handlers to check for both "messages" and "input" fields, as this will ensure content moderation and prompt injection detection work correctly for the OpenAI Responses API.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING