can1357/oh-my-pi · error

Gemini Files API upload finalization request failed

Error message

Gemini Files API upload finalization request failed

What it means

The second fetch — POST of the actual bytes to the X-Goog-Upload-URL with 'upload, finalize' — threw at the network level, so the original cause is replaced by this message. The file bytes failed to reach Google (or the connection dropped mid-transfer); HTTP error statuses produce a different message.

Source

Thrown at packages/coding-agent/src/blob-broker/provider-files-gemini.ts:132

			const uploadUrl = startResponse.headers.get("X-Goog-Upload-URL")?.trim();
			if (!uploadUrl)
				throw new Error("Gemini Files API upload initialization response is missing X-Goog-Upload-URL");

			let finalizeResponse: Response;
			try {
				finalizeResponse = await fetchImpl(uploadUrl, {
					method: "POST",
					headers: {
						"Content-Length": String(byteLength),
						"X-Goog-Upload-Command": "upload, finalize",
						"X-Goog-Upload-Offset": "0",
					},
					body: request.bytes,
					signal: request.signal,
				});
			} catch {
				throw new Error("Gemini Files API upload finalization request failed");
			}
			if (!finalizeResponse.ok) {
				throw new Error(`Gemini Files API upload finalization failed with HTTP ${finalizeResponse.status}`);
			}

			const file = parseFinalizedFile(await responseJson(finalizeResponse, "finalize"));
			return {
				provider: "google",
				id: file.name,
				uri: file.uri,
				mimeType: file.mimeType,
				bytes: byteLength,
				expiresAt: file.expiresAt,
				delete: {
					method: "DELETE",
					url: `${GEMINI_FILES_RESOURCE_URL}/${file.name}`,
					headers: { "x-goog-api-key": credential },
				},

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry the whole upload with backoff — resumable sessions may have expired, so re-run start + finalize.
  2. Check whether your AbortSignal fired (user cancel or your own timeout).
  3. Reduce file size or improve connection stability; check proxy body-size/timeout limits.
  4. For flaky networks, implement chunked resumable uploads yourself using the X-Goog-Upload-Offset protocol instead of single-shot finalize.
  5. Note the underlying error is swallowed; wrap fetchImpl to log the native cause.

Example fix

// before: single-shot, no retry
const handle = await client.upload({ bytes, mimeType });
// after: retry whole resumable flow on network failure
async function uploadWithRetry(req, attempts = 3) {
  try { return await client.upload(req); }
  catch (e) {
    if (attempts > 1 && String(e.message).includes("finalization request failed")) {
      await Bun.sleep(2000);
      return uploadWithRetry(req, attempts - 1);
    }
    throw e;
  }
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try {
  const handle = await client.upload(request);
} catch (error) {
  if (error instanceof Error && error.message.includes("finalization request failed")) {
    if (request.signal?.aborted) throw error;
    // network drop mid-body: restart the full resumable flow with backoff
    return uploadWithBackoff(request);
  } else throw error;
}

Prevention

When it happens

Trigger: fetchImpl() on the finalize request rejects: connection reset mid-upload (common with large bodies), request.signal aborted, TLS failure, DNS drop between the two requests, or body streaming failure.

Common situations: Uploading large files over unstable connections; timeouts/proxies killing long POSTs; user cancellation via AbortSignal; Wi-Fi/network switch during upload; request body exceeding an intermediary's size limit.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/ac393cf1c7eb3814. Report an issue: GitHub.