{"record":{"id":"afb35202a0210777","repo":"can1357/oh-my-pi","slug":"gemini-files-api-finalize-response-contains-an-inv","errorCode":null,"errorMessage":"Gemini Files API finalize response contains an invalid file.name","messagePattern":"Gemini Files API finalize response contains an invalid file\\.name","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/provider-files-gemini.ts","lineNumber":46,"sourceCode":"\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)) {\n\t\tthrow new Error(\"Gemini Files API finalize response contains an invalid file.expirationTime\");\n\t}\n\treturn { name, uri, mimeType, expiresAt };\n}\n\nfunction isOfficialGeminiModel(model: Model): boolean {\n\tif (model.provider !== \"google\" || model.api !== \"google-generative-ai\") return false;\n\ttry {\n\t\tconst baseUrl = new URL(model.baseUrl);\n\t\treturn (\n\t\t\tbaseUrl.protocol === \"https:\" &&\n\t\t\tbaseUrl.hostname === \"generativelanguage.googleapis.com\" &&","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/provider-files-gemini.ts#L28-L64","documentation":"parseFinalizedFile() throws this when file.name passes requireString but fails the format check /^files\\/[^/]+$/. The name is used both as the handle id and to build the DELETE URL (https://generativelanguage.googleapis.com/v1beta/files/{name}), so a malformed name would produce broken resource URLs downstream.","triggerScenarios":"Google's finalize payload returns a file.name that is empty after non-empty check, contains slashes beyond the files/ prefix, or uses a completely different id scheme (API version change).","commonSituations":"Google changing the Files API resource-name format in a new API version or preview; proxies/rewriters altering the response; hand-rolled mocks returning wrong-shaped names in tests.","solutions":["Log the actual file.name value returned to see how it differs from files/{id}.","Check the pinned API version (v1beta) against Google's current Files API docs for name-format changes.","Update to a package version that matches the current API response format.","If behind a proxy, verify it is not rewriting the JSON response body."],"exampleFix":"// before: assuming the name format\nconst handle = await client.upload(req);\n// after: capture raw payload for diagnosis\nconst raw = await finalizeResponse.text();\nconsole.error(\"file.name was:\", JSON.parse(raw)?.file?.name); // compare with /^files\\/[^/]+$/","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"function isValidGeminiFileName(name: unknown): name is string {\n  return typeof name === \"string\" && /^files\\/[^/]+$/.test(name);\n}","tryCatchPattern":"try {\n  const handle = await client.upload(request);\n} catch (error) {\n  if (error instanceof Error && error.message.includes(\"invalid file.name\")) {\n    logger.error(\"Gemini file.name format drifted\", { message: error.message });\n    // report/upgrade; do not use the malformed name for DELETE URLs\n  } else throw error;\n}","preventionTips":["Keep the package updated so name-format checks track current API versions","Disable response-rewriting middleware for API traffic","Assert mock fixtures use realistic files/{id} names","Never persist handles whose id fails the files/ pattern check"],"tags":["validation","api-response","schema-validation"],"backgroundTag":"invalid-resource-name","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}