session: Event.IsFinalResponse panics on a nil *genai.Part in Content.Parts
- 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
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