Skip to content

[Go] Index out of range panic in streaming mode with ThinkingConfig #3752

@kqns91

Description

@kqns91

Describe the bug

When using the Google AI plugin for streaming generation with structured output and ThinkingConfig, the application panics with:

panic: runtime error: index out of range [0] with length 0

Stack trace:

github.com/firebase/genkit/go/plugins/googlegenai.generate-range1(...)
    github.com/firebase/genkit/go@v1.1.0/plugins/googlegenai/gemini.go:315

Root cause:

In gemini.go lines 303-315, there's an index variable misuse:

for i, c := range chunk.Candidates {
    tc, err := translateCandidate(c)
    // ...
    chunks = append(chunks, c.Content.Parts[i])  // Bug: i is Candidates index, not Parts index
}

The variable i represents the index in the Candidates array, but the code uses it to access c.Content.Parts[i], which is unrelated. This causes a panic when:

  • c.Content.Parts is empty (length 0) but i=0 (first candidate)
  • c.Content.Parts has fewer elements than the Candidates array

To Reproduce

I've created a minimal reproduction case that triggers this bug with approximately 50% success rate.

Code:

package main

import (
    "context"
    "fmt"
    "os"
    "github.com/firebase/genkit/go/ai"
    "github.com/firebase/genkit/go/genkit"
    "github.com/firebase/genkit/go/plugins/googlegenai"
    "google.golang.org/genai"
)

func main() {
    ctx := context.Background()
    g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.GoogleAI{}))

    // Run multiple rapid requests to increase panic probability
    for i := 0; i < 10; i++ {
        testCase(ctx, g)
    }
}

func testCase(ctx context.Context, g *genkit.Genkit) error {
    type Output struct {
        Text string `json:"text"`
    }

    _, err := genkit.Generate(ctx, g,
        ai.WithModelName("googleai/gemini-2.5-flash"),
        ai.WithPrompt("Hi"),
        ai.WithConfig(&genai.GenerateContentConfig{
            ThinkingConfig: &genai.ThinkingConfig{
                ThinkingBudget: genai.Ptr(int32(0)),
            },
        }),
        ai.WithOutputType(Output{}),
        ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
            return nil
        }),
    )
    return err
}

How to run:

  1. Set GOOGLE_API_KEY environment variable
  2. Run the code multiple times (bug is intermittent)
  3. Panic typically occurs during rapid consecutive requests

Reproduction rate: Approximately 50% (observed 1 panic in 2 complete executions)

Expected behavior

The streaming callback should handle all Gemini API response structures gracefully, including chunks with empty Parts arrays, without panicking.

Screenshots

N/A

Runtime (please complete the following information):

  • Operating system: macOS
  • OS version: Darwin 24.6.0

Go version:

go version go1.25.2 darwin/arm64

Additional context

Proposed Fix:

Replace lines 303-315 in gemini.go with:

for _, c := range chunk.Candidates {
    tc, err := translateCandidate(c)
    if err != nil {
        return nil, err
    }
    err = cb(ctx, &ai.ModelResponseChunk{
        Content: tc.Message.Content,
    })
    if err != nil {
        return nil, err
    }
    // Safely iterate through all parts
    for _, part := range c.Content.Parts {
        chunks = append(chunks, part)
    }
}

Key changes:

  • Remove index variable i
  • Use nested loop to safely iterate all Parts
  • Handles empty Parts arrays correctly
  • Correctly handles multiple candidates

Related Issues:

A similar bug in tool usage was fixed in PR #2980 (related to issue #2978), but this streaming bug remains.


Thank you for maintaining this excellent project. Happy to provide additional information or submit a PR if helpful.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggo

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions