wavetermdev/waveterm · error
sse handler is nil
Error message
sse handler is nil
What it means
RunAnthropicChatStep receives a *sse.SSEHandlerCh used to stream Anthropic responses back to the caller. If the handler pointer is nil there is nowhere to deliver streaming events, so the function aborts immediately before any API call. This is a caller-contract violation guard, not an API-side failure.
Source
Thrown at pkg/aiusechat/anthropic/anthropic-backend.go:427
return sanitizeHostnameInError(fmt.Errorf("anthropic %s: %s", resp.Status, proxyErr.Error))
}
// Fall back to truncated raw response
msg := utilfn.TruncateString(strings.TrimSpace(string(slurp)), 120)
if msg == "" {
msg = "unknown error"
}
return sanitizeHostnameInError(fmt.Errorf("anthropic %s: %s", resp.Status, msg))
}
func RunAnthropicChatStep(
ctx context.Context,
sse *sse.SSEHandlerCh,
chatOpts uctypes.WaveChatOpts,
cont *uctypes.WaveContinueResponse,
) (*uctypes.WaveStopReason, *anthropicChatMessage, *uctypes.RateLimitInfo, error) {
if sse == 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 !uctypes.AreModelsCompatible(chat.APIType, chat.Model, chatOpts.Config.Model) {
return nil, nil, nil, fmt.Errorf("model mismatch: chat has %s, chatOpts has %s", chat.Model, chatOpts.Config.Model)
}
if chat.APIVersion != chatOpts.Config.APIVersion {
return nil, nil, nil, fmt.Errorf("API version mismatch: chat has %s, chatOpts has %s", chat.APIVersion, chatOpts.Config.APIVersion)
}View on GitHub (pinned to a4447c1563)
Solutions
- Create and pass an SSE handler: sse := sse.NewSSEHandlerCh(...) before calling RunAnthropicChatStep.
- If you do not need streamed output, use the appropriate non-streaming entry point instead of passing nil.
- Add a caller-side nil check/log to catch the misconfigured call site early.
Example fix
// before var sse *sse.SSEHandlerCh // nil stop, msg, rl, err := RunAnthropicChatStep(ctx, sse, chatOpts, cont) // after sse := sse.NewSSEHandlerCh(bufSize) stop, msg, rl, err := RunAnthropicChatStep(ctx, sse, chatOpts, cont)
Defensive patterns
Strategy: validation
Validate before calling
if sse == nil {
return fmt.Errorf("cannot run anthropic chat step: SSE handler not initialized")
} Type guard
func hasSSEHandler(sse *sse.SSEHandlerCh) bool { return sse != nil } Try / catch
stop, msg, rl, err := RunAnthropicChatStep(ctx, sse, chatOpts, cont)
if err != nil {
if err.Error() == "sse handler is nil" {
// fix call site: initialize the handler before retrying
}
return err
} Prevention
- Always construct the SSE handler immediately before the chat-step call in the same function.
- Never pass a possibly-nil handler variable; use a constructor that cannot return nil.
- Add a unit test covering the chat step call path.
When it happens
Trigger: Calling RunAnthropicChatStep (directly or via the anthropic backend chat path) passing sse = nil, e.g. constructing WaveChatOpts without initializing an SSEHandlerCh, or reusing a code path that skips stream setup for non-streaming calls.
Common situations: Refactored call sites that dropped the SSE handler argument; tests invoking the chat step without stream plumbing; wrappers that only sometimes allocate the handler depending on a 'stream' flag defaulting to false.
Related errors
- sse handler is nil
- sse handler is nil
- msg.error
- chatOpts.ClientId is required
- nil wshrpc passed to wshclient
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/bde9b2a05a8df887.
Report an issue: GitHub.