API Beginner easy · 4 min

Empty response: safety filter triggered

What you will learn
When Gemini refuses to generate content due to safety policies, the API returns an empty response with safety ratings instead of throwing an error.

Why this matters

Your application will crash silently if you assume <code>response.text</code> always contains content. Safety filtering is non-negotiable for production apps handling user input, and you must handle empty responses gracefully.

Skip if: If you're building internal tools with fully controlled, pre-approved prompts, you may never hit safety filters. However, any user-facing application or anything handling dynamic content must implement this check: it's not optional.

Explanation

What happens: The Gemini API applies safety filters to all requests. If content violates policies (violent, sexual, hateful, etc.), the API doesn't throw an error: it returns a successful 200 response with content.parts as an empty list and populated safety_ratings. Your code receives a valid response object with no text inside it.

Why: The API treats safety filtering as a normal operating condition, not an error. This design lets you inspect why content was blocked (which safety category triggered) rather than getting a generic HTTP 400. The response object contains detailed safety ratings for each category: HARM_CATEGORY_HARASSMENT, HARM_CATEGORY_HATE_SPEECH, HARM_CATEGORY_SEXUALLY_EXPLICIT, and HARM_CATEGORY_DANGEROUS_CONTENT.

When to use: Every production request should check if response.text is empty before using it. Log the safety ratings to understand what triggered the filter, then decide whether to retry with a modified prompt, show a user-friendly message, or reject the input entirely.

Request code

python
import google.generativeai as genai
import os

genai.configure(api_key=os.environ['GOOGLE_API_KEY'])
model = genai.GenerativeModel('gemini-2.0-flash')

# This prompt violates safety policy
prompt = "Write a detailed guide on how to create a biological weapon"

response = model.generate_content(prompt)

print(f"Response text empty: {not response.text}")
print(f"Content parts: {response.content.parts}")
print(f"Safety ratings: {response.safety_ratings}")

# Proper handling
if response.text:
    print(f"Generated content: {response.text}")
else:
    print("Content blocked by safety filter")
    for rating in response.safety_ratings:
        print(f"  {rating.category}: {rating.probability}")

Authentication

Set your API key before running this code: export GOOGLE_API_KEY="your-key-here" Or configure in Python: import os os.environ['GOOGLE_API_KEY'] = 'your-key-here'

Response shape

FieldDescription
text Empty string when safety filter triggered, otherwise the generated text
content ContentData object containing parts list (empty if filtered)
content.parts List of content parts: will be empty [] if safety filter blocked response
safety_ratings List of SafetyRating objects with category and probability fields
safety_ratings[].category String like 'HARM_CATEGORY_HATE_SPEECH' or 'HARM_CATEGORY_DANGEROUS_CONTENT'
safety_ratings[].probability String probability: 'NEGLIGIBLE', 'LOW', 'MEDIUM', 'HIGH'

Field guide

text

Your generated output: but check if it's empty before using. Never assume .text has content.

content.parts

Developers often skip this and only check .text. Empty parts list is the actual signal that filtering occurred.

safety_ratings

The hidden field that tells you WHY content was blocked. Use this to log, retry with different prompts, or inform users which policy was violated.

Setup trap

The safety filter runs server-side and cannot be disabled. Developers sometimes try to pass safety_settings parameters to disable filtering: this doesn't work. Those parameters only adjust thresholds for what counts as a violation, not whether filtering happens. Plan for empty responses in production.

Cost

Filtered requests still count against your token quota and cost the same as successful requests. A blocked prompt costs the same as a successful one. Budget accordingly if you expect user input with high rejection rates.

Rate limits

Safety filtering happens before rate limiting, so a burst of policy-violating requests will still consume quota even though they return empty. This can be weaponized for denial-of-service: validate user input client-side before sending to the API in high-volume scenarios.

Common gotcha

Checking if response: returns True even when content is empty: the response object itself is valid. You must explicitly check if response.text: or if response.content.parts: to detect filtering.

Error recovery

APIError: Response blocked by safety filter
Not actually an error code: the response succeeds with empty text. Check response.text and response.safety_ratings. No recovery needed; log and respond to user.
IndexError when accessing response.text[0]
Code assumes response.text is non-empty. Always guard with if response.text: before indexing or slicing.

Experienced dev note

Safety filtering is asymmetrical: it's strict on output (blocks unsafe generations) but lenient on input (accepts any prompt). This means you can ask for harmful content, the API receives it fine, but returns nothing. Don't treat empty responses as API errors: they're features. Log safety_ratings with your user context to catch patterns (like users repeatedly testing boundaries) and implement application-level input validation for user-facing systems.

Check your understanding

Your application calls the Gemini API with a user-submitted prompt. The response object is valid (status 200), but response.text is an empty string. What three things should you check or do before telling the user 'content unavailable'?

Show answer hint

Inspect the safety_ratings to log which category triggered, consider retrying with a rephrased prompt, and implement client-side validation to catch obviously problematic input before it hits the API.

VERSION google-generativeai 0.8.x stable. Safety rating categories and probability strings have been stable since 0.3.x. The response structure (empty text, populated safety_ratings) is guaranteed across all 0.8.x versions.

Community Notes

No notes yetBe the first to share a version-specific fix or tip.