gofr-dev/gofr · error
stream source panicked
Error message
stream source panicked
What it means
errStreamPanic is declared in pkg/gofr/http/stream.go and signals that the Streamer source backing an HTTP stream panicked during execution. The streaming machinery recovers the panic (see readStream) and converts it into this error so the client connection is terminated cleanly instead of crashing the process.
Source
Thrown at pkg/gofr/http/stream.go:19
package http
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
resTypes "gofr.dev/pkg/gofr/http/response"
)
const (
defaultHeartbeat = 15 * time.Second
streamWriteWindow = 30 * time.Second
)
var (
errStreamPanic = errors.New("stream source panicked")
errNilStream = errors.New("stream source is nil")
errStreamCorrupt = errors.New("stream value could not be encoded")
)
// handleStream drains s.Source to the client, flushing after every write. It pulls values on
// demand so a slow client throttles the producer, sends a periodic keep-alive so a dropped client
// is detected while idle, bounds each write with a deadline, and always closes the source — leaving
// no goroutine behind when the client disconnects mid-stream. A Source that also honors its own
// context cancels promptly even without a heartbeat.
func (r Responder) handleStream(s resTypes.Stream) {
if s.Source == nil {
r.w.WriteHeader(http.StatusInternalServerError)
_, _ = r.w.Write([]byte(`{"error":{"message":"` + errNilStream.Error() + `"}}` + "\n"))
return
}
rc := http.NewResponseController(r.w)View on GitHub (pinned to 187eb24962)
Solutions
- Find the panic value in the wrapped error message ('stream source panicked: <p>') to locate the faulting code
- Fix the underlying panic in your Streamer implementation
- Add defensive checks (nil guards, bounds checks) inside the source before it produces values
- Recover and convert expected failure modes into ordinary errors inside your source instead of panicking
Example fix
// before
func (s *mySource) Stream(items chan<- any) {
for _, v := range s.data { // panics if s.data is nil
items <- v
}
}
// after
func (s *mySource) Stream(items chan<- any) {
if s.data == nil {
return
}
for _, v := range s.data {
items <- v
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if source == nil {
return errors.New("stream source must not be nil")
}
if err := source.Validate(); err != nil {
return err
} Type guard
func hasValidSource(s *Stream) bool {
return s != nil && s.Source != nil && !reflect.ValueOf(s.Source).IsNil()
} Try / catch
err := runStream(ctx, src)
if err != nil {
if errors.Is(err, errStreamPanic) {
log.Printf("stream source panicked: %v", err)
reconnectOrFallback(ctx)
return
}
return err
} Prevention
- Never panic inside Streamer implementations; return errors instead
- Guard nil maps/slices and type assertions in source loops
- Test sources with edge-case data (nil fields, empty inputs)
- Keep the panic value from the wrapped error for diagnostics
When it happens
Trigger: A user-supplied resTypes.Streamer source panics (nil map write, index out of range, explicit panic) while readStream is pulling values; the recovered panic is wrapped as '%w: %v' with errStreamPanic and sent over the terminal error channel.
Common situations: Buggy custom stream generators (nil pointer dereference on business objects); sources that panic on channel close; data-dependent panics mid-stream such as division by zero or type assertions on unexpected values.
Related errors
AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01).
Data as JSON: /api/errors/c67ef056441d2d46.
Report an issue: GitHub.