wavetermdev/waveterm · error
sse handler is nil
Error message
sse handler is nil
What it means
RunGeminiChatStep streams Gemini responses through an SSE handler channel. It rejects a nil *sse.SSEHandlerCh at the top of the function because without a handler there is nowhere to deliver streaming events. This is a programmer-error guard: the caller must construct the SSE handler before starting the chat step.
Source
Thrown at pkg/aiusechat/gemini/gemini-backend.go:187
return nil, err
}
// Set headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-goog-api-key", opts.APIToken)
return req, nil
}
// RunGeminiChatStep executes a chat step using the Gemini API
func RunGeminiChatStep(
ctx context.Context,
sseHandler *sse.SSEHandlerCh,
chatOpts uctypes.WaveChatOpts,
cont *uctypes.WaveContinueResponse,
) (*uctypes.WaveStopReason, *GeminiChatMessage, *uctypes.RateLimitInfo, error) {
if sseHandler == nil {
return nil, nil, nil, errors.New("sse handler is nil")
}
// Get chat from store
chat := chatstore.DefaultChatStore.Get(chatOpts.ChatId)
if chat == nil {
return nil, nil, nil, fmt.Errorf("chat not found: %s", chatOpts.ChatId)
}
// Validate that chatOpts.Config match the chat's stored configuration
if chat.APIType != chatOpts.Config.APIType {
return nil, nil, nil, fmt.Errorf("API type mismatch: chat has %s, chatOpts has %s", chat.APIType, chatOpts.Config.APIType)
}
if chat.Model != chatOpts.Config.Model {
return nil, nil, nil, fmt.Errorf("model mismatch: chat has %s, chatOpts has %s", chat.Model, chatOpts.Config.Model)
}
// Context with timeout if provided
if chatOpts.Config.TimeoutMs > 0 {View on GitHub (pinned to a4447c1563)
Solutions
- Create the SSE handler before calling RunGeminiChatStep: handler := sse.NewSSEHandlerCh(...)
- Trace why the handler variable is nil at call time (initialization order, error path, or copy of a nil pointer)
- If the handler creation itself failed, abort the chat step instead of proceeding with nil
- In tests, provide a real or mocked SSEHandlerCh rather than nil
Example fix
// before var sseHandler *sse.SSEHandlerCh go RunGeminiChatStep(ctx, sseHandler, chatOpts, cont) // after sseHandler := sse.NewSSEHandlerCh(ctx) go RunGeminiChatStep(ctx, sseHandler, chatOpts, cont)
Defensive patterns
Strategy: type-guard
Validate before calling
if sseHandler == nil {
return fmt.Errorf("cannot run Gemini chat step: SSE handler not initialized")
} Type guard
func handlerReady(h *sse.SSEHandlerCh) bool { return h != nil } Try / catch
if err != nil && err.Error() == "sse handler is nil" {
return fmt.Errorf("internal wiring bug: chat step started without an SSE handler")
} Prevention
- Always create the SSE handler immediately before launching the chat step goroutine
- Never share or reuse handlers across chat steps; create one per request
- Add an assertion/test that the handler is non-nil at every RunGeminiChatStep call site
When it happens
Trigger: RunGeminiChatStep is called with sseHandler == nil — e.g. a caller forgot to create sse.NewSSEHandlerCh, a refactor reordered initialization so the handler is created after the chat step starts, or a nil handler was passed through from an error path.
Common situations: Wiring a new backend integration and skipping handler setup; concurrency bug where the handler variable is still nil when the goroutine launches; tests calling RunGeminiChatStep directly without a handler stub.
Related errors
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/7cb6e97a6b35c1e3.
Report an issue: GitHub.