You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the Gemini provider's realtime (non-batch) inference path, a safety-blocked prompt, a candidate that stopped for a non-STOP reason (safety, recitation, etc.) with no content, or a response containing only non-text parts is returned as a successful, empty extraction instead of raising.
google.genai's GenerateContentResponse.text (_get_text() in the SDK) returns None, not an exception, when:
not self.candidates — the prompt itself was blocked (prompt_feedback.block_reason set),
candidates[0].content or .content.parts is empty — the candidate stopped for a non-STOPfinish_reason (SAFETY, RECITATION, PROHIBITED_CONTENT, etc.) with no output, or
the response contains only non-text parts (e.g. a function call).
ScoredOutput.output is typed str | None, so None is accepted and returned with score=1.0. Downstream, this is reported as a successful empty extraction — the caller gets no signal that anything was blocked, and the block/refusal reason (prompt_feedback.block_reason, candidates[0].finish_reason) is silently discarded.
This is the same class of bug reported for the OpenAI realtime path in #491 (fixed for OpenAI in #496). #491 explicitly noted: "The Gemini realtime path has the same class of gap on a safety-blocked response.text (version-dependent); worth folding the same guard in there too." PR #496 only touched openai.py, so this side was never addressed.
Expected behavior
When response.text is None, _process_single_prompt raises InferenceRuntimeError with the available diagnostic (block reason / finish reason), mirroring the fix already applied to the OpenAI provider.
Actual behavior
_process_single_prompt returns ScoredOutput(score=1.0, output=None). The caller receives a "successful" extraction with no content; the actual block/refusal reason is lost.
Steps to reproduce the issue
Configure GeminiLanguageModel and issue a prompt that trips Gemini's safety filters, or otherwise produces a candidate with a non-STOP finish reason and no text content.
The SDK's response.text evaluates to None (confirmed directly against google-genai 2.11.0's GenerateContentResponse._get_text).
Observe _process_single_prompt returns ScoredOutput(score=1.0, output=None) instead of raising — the extraction is reported as a successful empty result.
Any additional content
Verified without live API: mock models.generate_content to return a response with text=None, prompt_feedback.block_reason or candidates[0].finish_reason set, and assert _process_single_prompt raises InferenceRuntimeError surfacing the reason (mirrors the existing OpenAI refusal test added in Handle OpenAI refusals and missing message content #496).
Describe the overall issue and situation
In the Gemini provider's realtime (non-batch) inference path, a safety-blocked prompt, a candidate that stopped for a non-
STOPreason (safety, recitation, etc.) with no content, or a response containing only non-text parts is returned as a successful, empty extraction instead of raising.langextract/providers/gemini.py(_process_single_prompt):google.genai'sGenerateContentResponse.text(_get_text()in the SDK) returnsNone, not an exception, when:not self.candidates— the prompt itself was blocked (prompt_feedback.block_reasonset),candidates[0].contentor.content.partsis empty — the candidate stopped for a non-STOPfinish_reason(SAFETY,RECITATION,PROHIBITED_CONTENT, etc.) with no output, orScoredOutput.outputis typedstr | None, soNoneis accepted and returned withscore=1.0. Downstream, this is reported as a successful empty extraction — the caller gets no signal that anything was blocked, and the block/refusal reason (prompt_feedback.block_reason,candidates[0].finish_reason) is silently discarded.This is the same class of bug reported for the OpenAI realtime path in #491 (fixed for OpenAI in #496). #491 explicitly noted: "The Gemini realtime path has the same class of gap on a safety-blocked
response.text(version-dependent); worth folding the same guard in there too." PR #496 only touchedopenai.py, so this side was never addressed.Expected behavior
When
response.text is None,_process_single_promptraisesInferenceRuntimeErrorwith the available diagnostic (block reason / finish reason), mirroring the fix already applied to the OpenAI provider.Actual behavior
_process_single_promptreturnsScoredOutput(score=1.0, output=None). The caller receives a "successful" extraction with no content; the actual block/refusal reason is lost.Steps to reproduce the issue
GeminiLanguageModeland issue a prompt that trips Gemini's safety filters, or otherwise produces a candidate with a non-STOPfinish reason and no text content.response.textevaluates toNone(confirmed directly againstgoogle-genai2.11.0'sGenerateContentResponse._get_text)._process_single_promptreturnsScoredOutput(score=1.0, output=None)instead of raising — the extraction is reported as a successful empty result.Any additional content
models.generate_contentto return a response withtext=None,prompt_feedback.block_reasonorcandidates[0].finish_reasonset, and assert_process_single_promptraisesInferenceRuntimeErrorsurfacing the reason (mirrors the existing OpenAI refusal test added in Handle OpenAI refusals and missing message content #496).mainat commitb5fe0ba.