Connect Timeout error when streaming via async client

Hi ! currently getting a ConnectTimeout error when using the async client with files SDK. Only started appearing 5 days ago. No code changes have happened before the error started appearing. Adding in a simple reproduction.

Environment details

  • Programming language: Python
  • OS: Ubuntu
  • Language runtime version: Python3.9
  • Package version: google-genai==1.4.0

Steps to reproduce

  1. Upload a PDF file to the /upload endpoint.
  2. Call /generate with URIs retrieved from /upload endpoint
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse, StreamingResponse
from sse_starlette.sse import ServerSentEvent, EventSourceResponse
from typing import List, AsyncGenerator
import os
import tempfile
import shutil
from pathlib import Path
from google import genai
from google.genai import types
from pydantic import BaseModel
import asyncio
import time
import json

app = FastAPI()

# Configure Gemini clients
client = genai.Client(api_key="your_key")

class FileURI(BaseModel):
	uri: str
	mime_type: str

class ContentRequest(BaseModel):
	uris: List[FileURI]
	user_input: str

async def upload_single_file(file: UploadFile, chat_id: str) -> types.File:
	# Create a temporary directory structure matching S3 path
	temp_dir = tempfile.mkdtemp()
	temp_path = os.path.join(temp_dir, "files", chat_id)
	os.makedirs(temp_path, exist_ok=True)

	file_path = os.path.join(temp_path, file.filename)

	try:
		# Save the file temporarily
		with open(file_path, 'wb') as f:
			content = await file.read()
			f.write(content)
			await file.seek(0)

		result = client.files.upload(
			file=file_path,
			config=types.UploadFileConfig(
				display_name=file.filename,
				mime_type=file.content_type
			)
		)
		return result

	finally:
		# Clean up temporary directory
		shutil.rmtree(temp_dir)

@app.post("/upload/")
async def upload_files(files: List[UploadFile] = File(...)):
	"""
	Upload multiple files to Gemini and return their URIs
	"""
	try:
		uris = []
		# Generate a unique chat ID for this batch of uploads
		chat_id = str(hash(str(files) + str(time.time())))
		
		for file in files:
			uploaded_file = await upload_single_file(file, chat_id)
			uris.append({
				"uri": uploaded_file.uri,
				"mime_type": uploaded_file.mime_type
			})
		
		return JSONResponse(
			content={"uris": uris},
			status_code=200
		)
	except Exception as e:
		raise HTTPException(status_code=500, detail=str(e))

async def generate_content_stream(request: ContentRequest) -> AsyncGenerator[ServerSentEvent, None]:
	"""
	Generator function that streams response chunks from Gemini
	"""
	try:
		file_parts = []
		for uri in request.uris:
			file_parts.append(
				types.Part.from_uri(
					file_uri=uri.uri,
					mime_type=uri.mime_type
				)
			)

		# Prepare the content list with user input and files
		contents = [
			types.Content(
				parts=[
					# go through URIs and add them to the content
					*file_parts,
					types.Part(
						text=request.user_input
					)
				]
			)
		]
		
		# Get the stream iterator from the async client
		response_iterator = await client.aio.models.generate_content_stream(
			model='gemini-2.0-flash',
			contents=contents
		)
		
		# Now iterate through the stream
		async for chunk in response_iterator:
			if chunk.text:
				print(chunk.text)
				yield ServerSentEvent(data=chunk.text)
				
		# Send a completion event
		yield ServerSentEvent(data="[DONE]")
			
	except Exception as e:
		print(e)
		yield ServerSentEvent(data=json.dumps({"error": str(e)}))
		yield ServerSentEvent(data="[DONE]")

@app.post("/generate/")
async def generate_content(request: ContentRequest):
	"""
	Generate content based on provided URIs and user input using Gemini
	"""
	return EventSourceResponse(
		generate_content_stream(request)
	)

if __name__ == "__main__":
	import uvicorn
	uvicorn.run(app, host="0.0.0.0", port=8000)