Environment details
- Programming language: Go
- OS: Linux (also reproducible on macOS)
- Language runtime version: go 1.24.4
- Package version: google.golang.org/genai v1.42.0
Steps to reproduce
- Create a client with
HTTPOptions.Timeout set:
timeout := 5 * time.Minute
client, _ := genai.NewClient(ctx, &genai.ClientConfig{
Backend: genai.BackendVertexAI,
Credentials: creds,
HTTPOptions: genai.HTTPOptions{Timeout: &timeout},
})
- Make a streaming request:
chat, _ := client.Chats.Create(ctx, "gemini-2.0-flash", config, nil)
count := 0
for chunk, err := range chat.SendMessageStream(ctx, genai.Part{Text: "Write a long poem"}) {
if err != nil { log.Fatal(err) }
count++
}
fmt.Println(count) // prints 1, expected 3-5+
- Remove the timeout — all chunks arrive correctly.
Demonstrated by the test attached to #688
Root cause
In api_client.go, sendStreamRequest creates a timeout context and immediately defers cancel:
requestContext, cancel = context.WithTimeout(ctx, *timeout)
defer cancel()
sendStreamRequest returns after setting up the bufio.Scanner but before iterateResponseStream consumes any chunks. The deferred cancel() fires on return, killing the HTTP connection. The first chunk may be buffered so it succeeds, but subsequent Scan() calls fail with "context canceled".
Suggested fix
Transfer ownership of cancel from sendStreamRequest's defer to responseStream, and call it in iterateResponseStream's cleanup defer alongside resp.Body.Close(). I have a PR with this fix and a regression test: #688
Environment details
Steps to reproduce
HTTPOptions.Timeoutset:Demonstrated by the test attached to #688
Root cause
In api_client.go, sendStreamRequest creates a timeout context and immediately defers cancel:
sendStreamRequestreturns after setting up thebufio.Scannerbut beforeiterateResponseStreamconsumes any chunks. The deferredcancel()fires on return, killing the HTTP connection. The first chunk may be buffered so it succeeds, but subsequentScan()calls fail with "context canceled".Suggested fix
Transfer ownership of cancel from sendStreamRequest's defer to responseStream, and call it in iterateResponseStream's cleanup defer alongside resp.Body.Close(). I have a PR with this fix and a regression test: #688