Is there an existing issue for this? Searched "fileData", "file_data", "FileData UnknownPart" in firebase/flutterfire. None found. #17615 (Files API support) is related but about sending files, not parsing them.
Which plugins are affected? AI
Which platforms are affected? All (pure Dart parsing in firebase_ai)
Description
parsePart in packages/firebase_ai/firebase_ai/lib/src/content.dart matches every part type by its lowerCamelCase JSON key (functionCall, executableCode, codeExecutionResult, inlineData, text), except file data, which is matched only as snake_case:
// content.dart, firebase_ai 4.0.0 (same on main)
if (jsonObject.containsKey('inlineData')) { ... } // line 157, camelCase
...
case {
'file_data': { // line 178, snake_case
'file_uri': final String fileUri,
'mime_type': final String mimeType,
}
}:
return FileData(mimeType, fileUri, ...);
...
return UnknownPart(jsonObject); // line 189
FileData.toJson() also emits snake_case ({'file_data': {'file_uri': ..., 'mime_type': ...}}, line 506). Requests work because the API accepts both the proto field name and the lowerCamelCase form on input. Responses are the problem: the Gemini API serialises Part.fileData as fileData / fileUri / mimeType (see the FileData object in the REST reference, https://ai.google.dev/api/caching#FileData), so a fileData part in a model response never matches the file_data case and falls through to UnknownPart. Callers that switch on FileData to read fileUri never see one; they get an UnknownPart whose data carries the raw map.
Expected: parsePart recognises fileData with fileUri / mimeType (and ideally keeps accepting file_data for compatibility), consistent with how inlineData is handled.
Reproducing the issue
parsePart is not exported, so the shortest reproduction is a canned response through GenerativeModel with a custom http.Client (no Firebase project needed beyond setupFirebaseCoreMocks() from firebase_core_platform_interface/test.dart):
// in a flutter test
final client = _FakeClient(jsonEncode({
'candidates': [
{
'content': {
'role': 'model',
'parts': [
{'fileData': {'fileUri': 'gs://bucket/image.png', 'mimeType': 'image/png'}},
],
},
'finishReason': 'STOP',
}
],
}));
final model = FirebaseAI.googleAI().generativeModel(
model: 'gemini-3.5-flash',
httpClient: client,
);
final response = await model.generateContent([Content.text('hi')]);
final part = response.candidates.first.content.parts.first;
print(part.runtimeType); // UnknownPart, expected FileData
print((part as UnknownPart).data); // {thought: false, fileData: {...}}
Changing the canned key to file_data / file_uri / mime_type yields a FileData part, which shows the casing is the only difference.
Firebase Core version: 4.15.0
Flutter Version: 3.47.2 stable
firebase_ai version: 4.0.0 (also reproduced by reading main at the time of filing)
Relevant Log Output: none, parsing silently falls back to UnknownPart.
Additional context and comments
Found while adding response-part handling in the Genkit Dart plugin for Firebase AI Logic; a test there now pins the current behaviour so an upstream fix shows up as a test change. Happy to send a PR: add a 'fileData': {'fileUri': ..., 'mimeType': ...} case alongside the existing file_data case, plus a unit test for both spellings.
Is there an existing issue for this? Searched "fileData", "file_data", "FileData UnknownPart" in firebase/flutterfire. None found. #17615 (Files API support) is related but about sending files, not parsing them.
Which plugins are affected? AI
Which platforms are affected? All (pure Dart parsing in
firebase_ai)Description
parsePartinpackages/firebase_ai/firebase_ai/lib/src/content.dartmatches every part type by its lowerCamelCase JSON key (functionCall,executableCode,codeExecutionResult,inlineData,text), except file data, which is matched only as snake_case:FileData.toJson()also emits snake_case ({'file_data': {'file_uri': ..., 'mime_type': ...}}, line 506). Requests work because the API accepts both the proto field name and the lowerCamelCase form on input. Responses are the problem: the Gemini API serialisesPart.fileDataasfileData/fileUri/mimeType(see theFileDataobject in the REST reference, https://ai.google.dev/api/caching#FileData), so afileDatapart in a model response never matches thefile_datacase and falls through toUnknownPart. Callers that switch onFileDatato readfileUrinever see one; they get anUnknownPartwhosedatacarries the raw map.Expected:
parsePartrecognisesfileDatawithfileUri/mimeType(and ideally keeps acceptingfile_datafor compatibility), consistent with howinlineDatais handled.Reproducing the issue
parsePartis not exported, so the shortest reproduction is a canned response throughGenerativeModelwith a customhttp.Client(no Firebase project needed beyondsetupFirebaseCoreMocks()fromfirebase_core_platform_interface/test.dart):Changing the canned key to
file_data/file_uri/mime_typeyields aFileDatapart, which shows the casing is the only difference.Firebase Core version: 4.15.0
Flutter Version: 3.47.2 stable
firebase_ai version: 4.0.0 (also reproduced by reading
mainat the time of filing)Relevant Log Output: none, parsing silently falls back to
UnknownPart.Additional context and comments
Found while adding response-part handling in the Genkit Dart plugin for Firebase AI Logic; a test there now pins the current behaviour so an upstream fix shows up as a test change. Happy to send a PR: add a
'fileData': {'fileUri': ..., 'mimeType': ...}case alongside the existingfile_datacase, plus a unit test for both spellings.