{"record":{"id":"45a63895537b42de","repo":"can1357/oh-my-pi","slug":"gemini-files-api-upload-initialization-response-is","errorCode":null,"errorMessage":"Gemini Files API upload initialization response is missing X-Goog-Upload-URL","messagePattern":"Gemini Files API upload initialization response is missing X-Goog-Upload-URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/provider-files-gemini.ts","lineNumber":117,"sourceCode":"\t\t\t\t\t\t\"X-Goog-Upload-Command\": \"start\",\n\t\t\t\t\t\t\"X-Goog-Upload-Header-Content-Length\": String(byteLength),\n\t\t\t\t\t\t\"X-Goog-Upload-Header-Content-Type\": request.mimeType,\n\t\t\t\t\t\t\"X-Goog-Upload-Protocol\": \"resumable\",\n\t\t\t\t\t\t\"x-goog-api-key\": credential,\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify(request.filename ? { file: { display_name: request.filename } } : { file: {} }),\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 initialization request failed\");\n\t\t\t}\n\t\t\tif (!startResponse.ok) {\n\t\t\t\tthrow new Error(`Gemini Files API upload initialization failed with HTTP ${startResponse.status}`);\n\t\t\t}\n\n\t\t\tconst uploadUrl = startResponse.headers.get(\"X-Goog-Upload-URL\")?.trim();\n\t\t\tif (!uploadUrl)\n\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}`);","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/provider-files-gemini.ts#L99-L135","documentation":"The start request succeeded (2xx) but the response is missing the X-Goog-Upload-URL header, which carries the resumable session URL the client must POST the bytes to. Without it the upload cannot proceed, so the client fails explicitly rather than POSTing to undefined.","triggerScenarios":"startResponse.ok is true but headers.get(\"X-Goog-Upload-URL\") is null or blank — Google returned an unexpected 2xx body/header set, or an intermediary stripped the custom header.","commonSituations":"Proxy or gateway stripping non-standard X-Goog-* response headers; a Google API behavior change; a fetch implementation that drops/case-mangles header names (custom FetchImpl or polyfill); Google incident returning 200 with an error body.","solutions":["Check for proxies/gateways that strip X-Goog-* response headers and whitelist them.","Verify your fetch implementation preserves header case (Headers are case-insensitive per spec; polyfills may not be).","Log all response headers via a debug FetchImpl wrapper to see what Google actually returned.","Retry — a transient Google-side anomaly can produce a 2xx without the session URL.","Check Google's status dashboard / release notes for Files API changes."],"exampleFix":"// before: opaque failure\nawait client.upload(request);\n// after: log headers to diagnose\nconst debugFetch: FetchImpl = async (url, init) => {\n  const res = await fetch(url, init);\n  if (!res.headers.get(\"x-goog-upload-url\")) console.error(\"start headers:\", [...res.headers]);\n  return res;\n};","handlingStrategy":"fallback","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  const handle = await client.upload(request);\n} catch (error) {\n  if (error instanceof Error && error.message.includes(\"X-Goog-Upload-URL\")) {\n    // header stripped or API drift: fall back to base64-inline attachment or retry once\n    return inlineAttachmentFallback(request);\n  } else throw error;\n}","preventionTips":["Whitelist X-Goog-* response headers on proxies/gateways for API hosts","Use spec-compliant fetch (Headers are case-insensitive); avoid leaky polyfills","Log full response headers when diagnosing upload issues","Keep a non-Files-API upload fallback path in your integration"],"tags":["http-headers","upload","api-response"],"backgroundTag":"missing-response-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}