google / google/adk-go

session: Event.IsFinalResponse panics on a nil *genai.Part in Content.Parts

Open Beginner friendly
#1,607 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
8.8k
Forks
1k
Avg merge
3d 18h
Merged PRs (30d)
88

Description

## Describe the bug

`genai.Content.Parts` is a `[]*genai.Part`, so a nil element is representable
in content that comes from outside the library — a `null` array entry in JSON
(`"parts":[null]`), or content built by a non-genai model backend or a remote
agent. All three helpers behind `Event.IsFinalResponse` dereference each part
unchecked and panic with a nil pointer dereference: `hasFunctionCalls`,
`hasFunctionResponses`, and `hasTrailingCodeExecutionResult`.

This is the same defect class fixed for the `internal/utils` accessors in
#1556; the `session` helpers were left unguarded.

## Where

- `session/session.go:439` — `part.FunctionCall` in `hasFunctionCalls`
- `session/session.go:451` — `part.FunctionResponse` in `hasFunctionResponses`
- `session/session.go:464` — `lastPart.CodeExecutionResult` in
`hasTrailingCodeExecutionResult`

All three are called from `Event.IsFinalResponse` at `session/session.go:230`.

## Impact

`IsFinalResponse` runs per event on several hot paths: the base flow's
final-response bookkeeping (`internal/llminternal/base_flow.go:141`), the
`OutputKey` save in `agent/llmagent` (`agent/llmagent/llmagent.go:530`),
workflow agent nodes (`workflow/agent_node.go:220`), the console launcher
(`cmd/launcher/console/console.go:252`), and the logging plugin
(`plugin/loggingplugin/logging_plugin.go:158`). One stored or received event
carrying a nil part panics the whole process on any of them — including the
read path, since a `null` part persisted to a session backend decodes back
into a nil `*genai.Part`.

## How to reproduce

```go
func TestIsFinalResponseSkipsNilParts(t *testing.T) {
var event session.Event
if err := json.Unmarshal([]byte(
`{"content":{"role":"model","parts":[null,{"text":"hi"}]}}`),
&event); err != nil {
t.Fatalf("json.Unmarshal failed: %v", err)
}
_ = event.IsFinalResponse() // panics: nil pointer dereference at session.go:439
}
```

## Suggested fix

Skip nil parts in `hasFunctionCalls` and `hasFunctionResponses`
(`part != nil &&`), and nil-check `lastPart` in
`hasTrailingCodeExecutionResult`. A nil part carries no function call,
response, or code execution result, so skipping it preserves the helpers'
contract — the same treatment the rest of the codebase already gives nil parts
(`internal/utils` accessors, `agent/remoteagent/v2`, `workflow`).

Contributor guide

Open the contributing guide

Research direction

Start in session/session.go at Event.IsFinalResponse and the three helpers it calls. Run the JSON reproduction with a null part, then add regression coverage for nil elements in session events. Done means IsFinalResponse completes without panicking and nil parts contribute no function call, response, or code execution result.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.