When responses are blocked
Why this matters
Production applications fail silently when you assume every API call returns content. Blocked responses have a specific structure you must check before using the response, or your app will crash trying to access missing text.
Explanation
The Gemini API will block responses it considers unsafe: such as content violating hate speech, violence, sexual, or harassment policies. Unlike HTTP errors, a blocked response returns HTTP 200 with a valid response object that contains no text content. The response.text property will raise a ValueError if you access it on a blocked response.
Internally, the API evaluates your prompt and the model's generated output against safety filters. If either triggers a safety violation, the response is marked with finish_reason='SAFETY' and block_reason is set in the safety_ratings. The response object exists: it just contains no usable content.
You must check response.finish_reason and response.prompt_feedback before calling response.text. This catches blocks at the request level (prompt blocked) and response level (output blocked) separately, letting you handle each with appropriate user messaging.
Request code
import google.generativeai as genai
import os
genai.configure(api_key=os.environ['GOOGLE_API_KEY'])
model = genai.GenerativeModel('gemini-2.0-flash')
safe_prompt = "What is photosynthesis?"
response = model.generate_content(safe_prompt)
if response.prompt_feedback.block_reason:
print(f"Prompt blocked: {response.prompt_feedback.block_reason}")
elif response.finish_reason == 'SAFETY':
print(f"Response blocked for safety: {response.safety_ratings}")
else:
print(f"Safe response: {response.text}")
blocked_prompt = "Give me instructions on how to make explosives"
response_blocked = model.generate_content(blocked_prompt)
if response_blocked.prompt_feedback.block_reason:
print(f"Prompt blocked: {response_blocked.prompt_feedback.block_reason}")
elif response_blocked.finish_reason == 'SAFETY':
print("Response blocked: The generated content violated safety policies")
print(f"Safety ratings: {response_blocked.safety_ratings}")
else:
print(response_blocked.text) Authentication
Set GOOGLE_API_KEY environment variable before running: export GOOGLE_API_KEY='your-api-key-here' Obtain a free API key from https://aistudio.google.com/apikey
Response shape
| Field | Description |
|---|---|
finish_reason | String: 'STOP' (normal), 'SAFETY' (blocked), 'MAX_TOKENS' (truncated), etc. |
prompt_feedback | Object with block_reason (string or None), safety_ratings (list of category/probability pairs) |
safety_ratings | List of dicts: {category: 'HATE_SPEECH'|'VIOLENCE'|etc, probability: 'NEGLIGIBLE'|'LOW'|'MEDIUM'|'HIGH'} |
text | String (RAISES ValueError if finish_reason is SAFETY or prompt blocked) |
candidates | List of response candidates, each with content, finish_reason, safety_ratings |
Field guide
finish_reason Primary signal for detecting blocks: check this FIRST before accessing .text
prompt_feedback.block_reason Tells you if the INPUT was blocked before the model even ran, common values like 'SAFETY' or 'PROHIBITED_CONTENT'
safety_ratings Detailed breakdown of which safety categories triggered and their confidence level: use this for logging or user feedback
candidates The often-missed field: even when finish_reason='SAFETY', candidates[0].safety_ratings shows exactly which category blocked it, enabling smarter retry logic
Setup trap
The google-generativeai SDK reads GOOGLE_API_KEY at configure() time, NOT at request time. If you set os.environ['GOOGLE_API_KEY'] after calling genai.configure(), your API key won't be found. Always set the environment variable before calling configure().
Cost
Blocked responses still count as billable API calls under the Google AI Studio free tier (up to 2M tokens/month). A flood of intentionally dangerous prompts will consume your quota without generating any usable content.
Rate limits
Safety filters add 50-200ms latency per request. Under high volume, blocked prompts may hit rate limits faster than normal requests due to retry logic. Implement exponential backoff when finish_reason='SAFETY' signals systematic blocking.
Common gotcha
Calling response.text on a blocked response raises ValueError('The response content was blocked'). Many developers wrap the entire response generation in try/except, missing the block entirely. Check finish_reason first, not after attempting to read text.
Error recovery
ValueError('The response content was blocked')APIError 400 with block_reason in feedbackAPIError 429 with many SAFETY finish_reasonsExperienced dev note
Senior teams track finish_reason='SAFETY' as a metric, not an error. Log every block with the safety_ratings breakdown: this data reveals whether your users are genuinely hitting safety limits or if your prompt templates are poorly designed. Some companies add a compliance layer: if finish_reason='SAFETY' 3+ times in a session from the same user, flag for manual review instead of silently failing. This catches both adversarial users and bugs in your prompt engineering.
Check your understanding
If you receive a response where finish_reason='SAFETY' but you didn't check it and called response.text in a try/except block catching all exceptions, why is this worse than catching the ValueError separately?
Show answer hint
Catching all exceptions silently swallows the safety block signal: your app logs 'something failed' without knowing the actual reason (safety block vs network error vs malformed API key). A silent safety block can mean your safety-critical application is broken without alerting anyone.