Repository navigation
Description
Environment details
- Programming language: Python
- OS: Windows 11
- Language runtime version: 3.14
- Package version: google-genai 2.33.0, aiohttp 3.14.5 (code unchanged on main)
Symptom
With aiohttp installed (the default async transport), client.aio.models.generate_content_stream fails when a single streamed chunk is larger than 4 MiB, e.g. an image model returning a large inlineData part (base64 of a ~3.1 MB+ image):
aiohttp.http_exceptions.LineTooLong: 400, message: Got more than 4194304 bytes when reading: b'data: {"candidates": [{"content": ... "inlineData" ...'
The text chunk before it is delivered; the image chunk is lost. The sync client (httpx) handles the same response.
Steps to reproduce
Self-contained script with a local SSE server, no API key (below):
$ python repro_genai_aiohttp.py 2000000 # 2.54 MiB base64: sync OK, async OK
$ python repro_genai_aiohttp.py 3500000 # 4.45 MiB base64
sync (httpx): ['text', 'inline_data 3500004 bytes']
async (aiohttp): ERR LineTooLong ... | chunks before error: ['text']
repro_genai_aiohttp.py
"""google-genai: async streaming fails when one SSE chunk is > 4 MiB (aiohttp path)."""
import asyncio, base64, json, threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from google import genai
IMG = base64.b64encode(b"\x89PNG" + b"\0" * (int(__import__("sys").argv[1]) if len(__import__("sys").argv) > 1 else 3_500_000)).decode()
CHUNKS = [
{"candidates": [{"content": {"role": "model", "parts": [{"text": "Here is your image."}]}}]},
{"candidates": [{"content": {"role": "model", "parts": [{"inlineData": {"mimeType": "image/png", "data": IMG}}]}, "finishReason": "STOP"}],
"usageMetadata": {"promptTokenCount": 5, "candidatesTokenCount": 1290, "totalTokenCount": 1295}},
]
BODY = b"".join(b"data: " + json.dumps(c).encode() + b"\r\n\r\n" for c in CHUNKS)
class H(BaseHTTPRequestHandler):
def do_POST(self):
self.rfile.read(int(self.headers["Content-Length"]))
self.send_response(200)
self.send_header("Content-Type", "text/event-stream")
self.send_header("Content-Length", str(len(BODY)))
self.end_headers()
self.wfile.write(BODY)
def log_message(self, *a): pass
srv = ThreadingHTTPServer(("127.0.0.1", 0), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
opts = {"base_url": f"http://127.0.0.1:{srv.server_port}"}
client = genai.Client(api_key="test", http_options=opts)
print(f"genai {genai.__version__}, base64 image = {len(IMG)/2**20:.2f} MiB")
def summarize(chunks):
return [("text" if p.text else f"inline_data {len(p.inline_data.data)} bytes") for c in chunks for p in c.candidates[0].content.parts]
try:
print("sync (httpx): ", summarize(list(client.models.generate_content_stream(model="gemini-3-pro-image-preview", contents="draw"))))
except Exception as e:
print("sync (httpx): ERR", type(e).__name__, str(e)[:200])
async def main():
out = []
try:
async for c in await client.aio.models.generate_content_stream(model="gemini-3-pro-image-preview", contents="draw"):
out.append(c)
print("async (aiohttp):", summarize(out))
except Exception as e:
print("async (aiohttp): ERR", type(e).__name__, str(e)[:200], "| chunks before error:", summarize(out))
asyncio.run(main())Root cause
google/genai/_api_client.py:587-589 reads each SSE line with content.readline(max_line_length=READ_BUFFER_SIZE), and READ_BUFFER_SIZE = 2**22 (:147). This follows #2248: raising the limit moved the ceiling to 4 MiB but kept it.
Suggested fix
Don't cap SSE line length below what the API can return: accumulate content.iter_chunked() / iter_any() and split lines manually (no per-line cap, matching httpx), or pass a much larger limit. Non-streaming responses aren't affected.
Activity
fix: Remove the per-line length cap when reading aiohttp SSE streams
Venkaiahbabuneelam commentedon Oct 30, 2026
Hi @jaeminbird,
The issue stems from
google/genai/_api_client.pypassingmax_line_length=READ_BUFFER_SIZE(4 MiB) intoaiohttp'sreadline()during SSE stream processing, triggeringaiohttp.http_exceptions.LineTooLongwhen an inline base64 image chunk exceeds that boundary.As a temporary workaround until the streaming buffer handling is updated, you can run the sync client stream via
asyncio.to_thread, ashttpxdoes not impose the 4 MiB line limitation:python
import asyncio
from google import genai
client = genai.Client()
def fetch_stream():
return list(client.models.generate_content_stream(
model="gemini-3.5-flash",
contents="...",
))
async def main():
chunks = await asyncio.to_thread(fetch_stream)
for chunk in chunks:
# Process streamed chunks
pass
asyncio.run(main())
fix: Remove the per-line length cap when reading aiohttp SSE streams
fix: Remove the per-line length cap when reading aiohttp SSE streams
fix: Remove the per-line length cap when reading aiohttp SSE streams