"Gemini Empty Response? It's Usually a Blocked Request"
"A gemini empty response with no candidates is almost never a model bug — it's a safety block hiding behind HTTP 200. Here's how to detect it and fix your code."
Your code isn't broken, and the model didn't glitch. Gemini answered your request with a polite, successful-looking nothing — HTTP 200, no error, no candidates — and your integration treated it as success because nothing told it otherwise. That's the real shape of the gemini empty response no candidates problem: a safety block wearing a 200 OK disguise. The fix isn't a different prompt. It's a three-line habit: check candidates before you touch .text , read promptFeedback.blockReason , and turn blocks into named errors instead of empty strings. A Gemini empty response with no candidates: what it actually looks like In October 2026, a production Discourse AI instance hit this exact shape. Language detection on a short, harmless Swedish topic title came back from gemini-3.5-flash-lite as HTTP 200 with a single stream event and no candidates key at all : data: {"promptFeedback": {"blockReason": "PROHIBITED_CONTENT"}, "usageMetadata": {"promptTokenCount": 417, "totalTokenCount": 417}, "modelVersion": "gemini-3.5-flash-lite", "responseId": "..."} The request already carried BLOCK_NONE on all four safety categories — this block came from a separate hard-block list the threshold settings don't touch. Because the endpoint code only read candidates , the block became an empty reply, the topic never got a language, and a backfill job re-sent the doomed request every five minutes for days. Nothing in the admin UI showed a block had ever occurred. The Python version of the same trap looks like this: response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt) return {"reply": response.text} # 💥 ValueError when the response was blocked On a blocked response, response.text raises ValueError: The response.text quick accessor…