{"record":{"id":"33da51bc6bf64a8e","repo":"can1357/oh-my-pi","slug":"gemini-files-api-upload-finalization-failed-with-h","errorCode":null,"errorMessage":"Gemini Files API upload finalization failed with HTTP ${finalizeResponse.status}","messagePattern":"Gemini Files API upload finalization failed with HTTP (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/provider-files-gemini.ts","lineNumber":135,"sourceCode":"\t\t\t\tthrow new Error(\"Gemini Files API upload initialization response is missing X-Goog-Upload-URL\");\n\n\t\t\tlet finalizeResponse: Response;\n\t\t\ttry {\n\t\t\t\tfinalizeResponse = await fetchImpl(uploadUrl, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Length\": String(byteLength),\n\t\t\t\t\t\t\"X-Goog-Upload-Command\": \"upload, finalize\",\n\t\t\t\t\t\t\"X-Goog-Upload-Offset\": \"0\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: request.bytes,\n\t\t\t\t\tsignal: request.signal,\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\"Gemini Files API upload finalization request failed\");\n\t\t\t}\n\t\t\tif (!finalizeResponse.ok) {\n\t\t\t\tthrow new Error(`Gemini Files API upload finalization failed with HTTP ${finalizeResponse.status}`);\n\t\t\t}\n\n\t\t\tconst file = parseFinalizedFile(await responseJson(finalizeResponse, \"finalize\"));\n\t\t\treturn {\n\t\t\t\tprovider: \"google\",\n\t\t\t\tid: file.name,\n\t\t\t\turi: file.uri,\n\t\t\t\tmimeType: file.mimeType,\n\t\t\t\tbytes: byteLength,\n\t\t\t\texpiresAt: file.expiresAt,\n\t\t\t\tdelete: {\n\t\t\t\t\tmethod: \"DELETE\",\n\t\t\t\t\turl: `${GEMINI_FILES_RESOURCE_URL}/${file.name}`,\n\t\t\t\t\theaders: { \"x-goog-api-key\": credential },\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tasync delete(handle: ProviderFileHandle): Promise<void> {","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/provider-files-gemini.ts#L117-L153","documentation":"This error is thrown by the Gemini Files API provider when the second phase of an upload (finalizing a file that was uploaded via the resumable upload URL) completes but the HTTP response status is not ok. The library distinguishes between a network/transport failure (a different message) and a completed HTTP exchange with a non-2xx status, surfacing the status code so developers can identify the server-side rejection reason.","triggerScenarios":"Calling upload() on the Gemini file client when fetch of the finalize request returns a non-ok status (e.g. 400, 401, 403, 404, 500) from the Gemini Files API finalize endpoint.","commonSituations":"Expired or wrong GOOGLE/GEMINI API key (401/403); finalize URL expired or already used (400/410); file exceeds size or quota limits (413/429); transient Gemini service errors (5xx); malformed metadata sent during finalize.","solutions":["Verify the Gemini API key (x-goog-api-key credential) is valid, active, and has Files API access; test with a simple models list call.","Check the status code in the message: 401/403 fix the key, 429 back off and retry respecting quota, 4xx do not blindly retry since the finalize session may be invalid.","Start a fresh upload (new resumable session) if the finalize URL was reused or the upload took longer than Gemini's session lifetime.","Retry on 5xx statuses after a delay; these are transient server-side failures.","Confirm network/proxy settings are not rewriting or blocking the request (e.g. corporate proxy returning 403)."],"exampleFix":"// before: retrying the same finalize on any failure\nawait retry(() => client.upload(request), 3);\n// after: inspect status, only retry transient errors\ntry {\n  await client.upload(request);\n} catch (err) {\n  if (!/HTTP 5\\d\\d/.test(err.message)) throw err; // 4xx: get a new upload session\n  await Bun.sleep(1000);\n  await client.upload(request);\n}","handlingStrategy":"retry","validationCode":"if (!credential?.trim()) throw new Error(\"Gemini API key required before upload\");\nif (typeof fetchImpl !== \"undefined\" && fetchImpl !== globalThis.fetch) console.warn(\"custom fetchImpl in use — check its error mapping\");","typeGuard":null,"tryCatchPattern":"try {\n  const handle = await geminiClient.upload(request);\n} catch (err) {\n  const m = /HTTP (\\d{3})/.exec(err.message ?? \"\");\n  const status = m ? Number(m[1]) : 0;\n  if (status >= 500 || status === 429) {\n    await Bun.sleep(2 ** attempt * 500);\n    return retryUpload();\n  }\n  throw err; // 4xx: fix key/session, don't retry\n}","preventionTips":["Keep the Gemini API key fresh and verify it with a cheap GET before long upload pipelines","Complete uploads within the resumable session lifetime; re-upload from scratch after long pauses","Retry only 5xx/429; treat 4xx as configuration errors","Check Gemini quota dashboards before bulk uploads"],"tags":["network","http","gemini","file-upload","api-key"],"backgroundTag":"file-upload-http-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}