-
Notifications
You must be signed in to change notification settings - Fork 553
Description
Describe the bug
runconfig.FromContext(ctx) returns nil when RunConfig is not set as a context value, causing a nil pointer dereference in internal/llminternal/base_flow.go:310:
useStream := runconfig.FromContext(ctx).StreamingMode == runconfig.StreamingModeSSERoot Cause
runconfig.ToContext() is never called anywhere in the ADK codebase. agent.Run() at agent/agent.go:181 copies ctx.RunConfig() into the internal invocationContext.runConfig struct field, but base_flow.go reads from the Go context via runconfig.FromContext(ctx) — not from the struct field. Since nobody puts the RunConfig into the context, FromContext always returns nil.
To Reproduce
Use agent.Run() with a custom InvocationContext implementation (not using the ADK runner). The nil dereference occurs on the first LLM call.
package main
import (
"context"
"fmt"
"google.golang.org/adk/agent"
"google.golang.org/adk/agent/llmagent"
"google.golang.org/adk/session"
"google.golang.org/genai"
)
// Custom InvocationContext (without using runner)
type myInvocationContext struct {
context.Context
agent agent.Agent
sess session.Session
userContent *genai.Content
ended bool
}
func (c *myInvocationContext) Agent() agent.Agent { return c.agent }
func (c *myInvocationContext) Artifacts() agent.Artifacts { return nil }
func (c *myInvocationContext) Memory() agent.Memory { return nil }
func (c *myInvocationContext) Session() session.Session { return c.sess }
func (c *myInvocationContext) InvocationID() string { return "test" }
func (c *myInvocationContext) Branch() string { return "main" }
func (c *myInvocationContext) UserContent() *genai.Content { return c.userContent }
func (c *myInvocationContext) RunConfig() *agent.RunConfig {
return &agent.RunConfig{StreamingMode: agent.StreamingModeNone}
}
func (c *myInvocationContext) EndInvocation() { c.ended = true }
func (c *myInvocationContext) Ended() bool { return c.ended }
func (c *myInvocationContext) WithContext(ctx context.Context) agent.InvocationContext {
newCtx := *c
newCtx.Context = ctx
return &newCtx
}
func main() {
// ... create agent, session ...
invCtx := &myInvocationContext{
Context: context.Background(),
agent: myAgent,
sess: sess,
userContent: genai.NewContentFromText("hello", genai.RoleUser),
}
// Panics at base_flow.go:310 — runconfig.FromContext returns nil
for event, err := range myAgent.Run(invCtx) {
fmt.Println(event, err)
}
}Stack trace
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation]
goroutine N [running]:
google.golang.org/adk/internal/llminternal.(*Flow).runLive(...)
.../adk/internal/llminternal/base_flow.go:310
Expected behavior
Should not panic. Either guard against nil RunConfig or inject the RunConfig into the context chain via runconfig.ToContext() in agent.Run().
Suggested Fix
Option A — guard nil dereference in base_flow.go:
var useStream bool
if rc := runconfig.FromContext(ctx); rc != nil {
useStream = rc.StreamingMode == runconfig.StreamingModeSSE
}Option B — have agent.Run() call runconfig.ToContext() when creating the internal invocation context, so the RunConfig flows through the context chain.
Version
- ADK v0.5.0 (also present in v0.4.0)
- Go 1.24