-
Notifications
You must be signed in to change notification settings - Fork 765
Description
Description
The finishMessage field from the Gemini API response is not being populated in Candidate objects when using generate_content_stream() with the Gemini API (mldev, not Vertex AI).
Environment
| Component | Value |
|---|---|
| Package | google-genai |
| Version | 1.62.0 |
| API | Gemini API (mldev) |
| Method | client.models.generate_content_stream() |
Current Behavior
When the API returns a response with finishMessage in the JSON:
{
"candidates": [{
"content": {},
"finishReason": "IMAGE_OTHER",
"finishMessage": "Unable to show the generated image. The model could not generate the image based on the prompt provided."
}]
}The Candidate object has:
candidate.finish_reason=FinishReason.IMAGE_OTHER(correctly populated)candidate.finish_message=None(not populated)
Expected Behavior
candidate.finish_message should contain the string value from the API response:
assert candidate.finish_message == "Unable to show the generated image. The model could not generate the image based on the prompt provided."Root Cause
In models.py, the _Candidate_from_mldev() function (line ~75) transforms the raw API response but does not map the finishMessage field:
def _Candidate_from_mldev(from_object, parent_object=None, root_object=None):
to_object: dict[str, Any] = {}
# ... other field mappings ...
# This mapping exists:
if getv(from_object, ['finishReason']) is not None:
setv(to_object, ['finish_reason'], getv(from_object, ['finishReason']))
# This mapping is MISSING:
# if getv(from_object, ['finishMessage']) is not None:
# setv(to_object, ['finish_message'], getv(from_object, ['finishMessage']))
# ... other field mappings ...
return to_objectReproduction
Minimal Example
from google import genai
from google.genai import types
client = genai.Client(api_key="YOUR_API_KEY")
stream = client.models.generate_content_stream(
model='gemini-2.0-flash-exp',
contents='Create a photorealistic image of a sunset over mountains',
config=types.GenerateContentConfig(
response_modalities=['IMAGE'],
image_config=types.ImageConfig(aspect_ratio='16:9')
)
)
for chunk in stream:
for candidate in chunk.candidates:
if candidate.finish_reason:
print(f"finish_reason: {candidate.finish_reason}")
print(f"finish_message: {candidate.finish_message}") # Always NoneVerification Test
Direct parsing with model_validate() works correctly, confirming the Candidate model supports the field:
from google.genai import types
# Simulate raw API response
raw_data = {
"content": {},
"finishReason": "IMAGE_OTHER",
"finishMessage": "Unable to show the generated image..."
}
# Direct parsing works
candidate = types.Candidate.model_validate(raw_data)
assert candidate.finish_message == "Unable to show the generated image..."
# But after SDK transformation, it's lost
from google.genai._common import get_value_by_path as getv
from google.genai._common import set_value_by_path as setv
def simulate_sdk_transformation(from_object):
to_object = {}
if getv(from_object, ['finishReason']) is not None:
setv(to_object, ['finish_reason'], getv(from_object, ['finishReason']))
# Missing: finishMessage mapping
return to_object
transformed = simulate_sdk_transformation(raw_data)
candidate2 = types.Candidate.model_validate(transformed)
assert candidate2.finish_message is None # Bug confirmedImpact
Users cannot access important diagnostic information about why content generation failed, making debugging difficult for:
- Image generation failures (
IMAGE_OTHER,IMAGE_SAFETY,IMAGE_PROHIBITED_CONTENT) - Content policy violations (
PROHIBITED_CONTENT,SAFETY,RECITATION) - Other failure scenarios where the API provides explanatory messages
This is especially problematic because finishMessage often contains actionable guidance like:
"Unable to show the generated image. The model could not generate the image based on the prompt provided. Try rephrasing the prompt."
Suggested Fix
Add the missing field mapping in _Candidate_from_mldev() around line 97:
if getv(from_object, ['finishReason']) is not None:
setv(to_object, ['finish_reason'], getv(from_object, ['finishReason']))
# Add this:
if getv(from_object, ['finishMessage']) is not None:
setv(to_object, ['finish_message'], getv(from_object, ['finishMessage']))Additional Context
- The
finish_messagefield is properly defined intypes.Candidate(line ~6484 intypes.py) - The Pydantic model uses
alias_generator=to_camelwhich correctly mapsfinish_messagetofinishMessage - The field appears in raw API responses (confirmed via logging)
- Only the transformation layer strips it out during streaming