{"record":{"id":"ef68ada65b83c3ff","repo":"can1357/oh-my-pi","slug":"gemini-files-api-finalize-response-is-missing-fi","errorCode":null,"errorMessage":"Gemini Files API finalize response is missing ${field}","messagePattern":"Gemini Files API finalize response is missing (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/provider-files-gemini.ts","lineNumber":34,"sourceCode":"function responseObject(value: unknown, context: string): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`Gemini Files API ${context} response is not a JSON object`);\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nasync function responseJson(response: Response, context: string): Promise<Record<string, unknown>> {\n\ttry {\n\t\treturn responseObject((await response.json()) as unknown, context);\n\t} catch (error) {\n\t\tif (error instanceof Error && error.message.startsWith(\"Gemini Files API\")) throw error;\n\t\tthrow new Error(`Gemini Files API ${context} response is not valid JSON`);\n\t}\n}\n\nfunction requireString(value: unknown, field: string): string {\n\tif (typeof value !== \"string\" || value.length === 0) {\n\t\tthrow new Error(`Gemini Files API finalize response is missing ${field}`);\n\t}\n\treturn value;\n}\n\nfunction parseFinalizedFile(payload: Record<string, unknown>): GeminiFileResource {\n\tconst file = responseObject(payload.file, \"finalize file\");\n\tconst state = requireString(file.state, \"file.state\");\n\tif (state !== \"ACTIVE\") throw new Error(\"Gemini Files API finalized file state is not ACTIVE\");\n\n\tconst name = requireString(file.name, \"file.name\");\n\tif (!/^files\\/[^/]+$/.test(name)) {\n\t\tthrow new Error(\"Gemini Files API finalize response contains an invalid file.name\");\n\t}\n\tconst uri = requireString(file.uri, \"file.uri\");\n\tconst mimeType = requireString(file.mimeType, \"file.mimeType\");\n\tconst expirationTime = requireString(file.expirationTime, \"file.expirationTime\");\n\tconst expiresAt = Date.parse(expirationTime);\n\tif (!Number.isFinite(expiresAt)) {","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/provider-files-gemini.ts#L16-L52","documentation":"requireString() throws this when a required field of the Gemini Files API finalize response's file object is absent, not a string, or an empty string. The library treats every one of file.state/file.name/file.uri/file.mimeType/file.expirationTime as mandatory to build a usable ProviderFileHandle, so a partially-populated response is rejected rather than propagated with undefined fields.","triggerScenarios":"parseFinalizedFile calls requireString(file.X, \"file.X\") and Google's finalize payload omits or empty-strings that field; the message interpolates the exact missing field name (e.g. 'finalize response is missing file.uri').","commonSituations":"Google changes or versions the v1beta Files API response schema; an unexpected state value shortens the payload; mock/stub responses in tests missing fields; a proxy stripping fields.","solutions":["Log the raw finalize response body to see which field is missing and what replaced it.","Check for Google API schema/preview-version changes in the v1beta Files API release notes.","Retry the upload — a finalize that lands before the file record is fully materialized can come back incomplete.","Update the package if a newer version adapts to a changed response shape."],"exampleFix":"// before: brittle full-schema assumption\ncatch (e) { throw new Error(\"upload failed: \" + e.message); }\n// after: inspect and adapt\nconst raw = await finalizeResponse.text();\nconsole.error(\"finalize payload:\", raw); // then fix expectations or file a bug","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"function isCompleteGeminiFile(f: unknown): f is { state: string; name: string; uri: string; mimeType: string; expirationTime: string } {\n  if (typeof f !== \"object\" || f === null) return false;\n  const o = f as Record<string, unknown>;\n  return [\"state\", \"name\", \"uri\", \"mimeType\", \"expirationTime\"].every(k => typeof o[k] === \"string\" && o[k].length > 0);\n}","tryCatchPattern":"try {\n  const handle = await client.upload(request);\n} catch (error) {\n  if (error instanceof Error && /missing file\\./.test(error.message)) {\n    logger.error(\"Gemini finalize response incomplete\", { field: error.message });\n    // pin/upgrade SDK version or report schema drift\n  } else throw error;\n}","preventionTips":["Pin to a maintained package version and read changelogs for Files API schema updates","Log raw API responses (debug FetchImpl) in staging to catch schema drift early","Keep mock responses in tests derived from real captured payloads","Monitor Google Workspace/AI release notes for v1beta Files API changes"],"tags":["schema-validation","api-response","parsing"],"backgroundTag":"missing-required-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}