gofr-dev/gofr · error
%w: %v
Error message
%w: %v
What it means
In readStream (pkg/gofr/http/stream.go:66) a panic raised by the stream source is recovered and wrapped as fmt.Errorf("%w: %v", errStreamPanic, p). The resulting error carries both the sentinel 'stream source panicked' and the original panic value, and is delivered on the terminal-error channel before items is closed.
Source
Thrown at pkg/gofr/http/stream.go:66
quit := make(chan struct{})
termErr := make(chan error, 1) // the source's terminal error, delivered once at end
defer close(quit)
go readStream(s.Source, items, quit, termErr)
r.pump(rc, s, items, termErr)
}
// readStream pulls from the source into items until the source is done or the consumer quits. A
// panic in the source is recovered into an error frame instead of crashing the server. The
// terminal error is sent before items is closed, so pump reads it safely after draining.
func readStream(src resTypes.Streamer, items chan<- any, quit <-chan struct{}, termErr chan<- error) {
var err error
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("%w: %v", errStreamPanic, p)
}
termErr <- err
close(items)
}()
for {
v, ok := src.Next()
if !ok {
err = src.Err()
return
}
select {
case items <- v:
case <-quit:
returnView on GitHub (pinned to 187eb24962)
Solutions
- Read the '%v' suffix of the error to see the actual panic value and pinpoint the bug
- Fix the panicking code in your Streamer implementation
- Add nil/type checks before touching data inside the source loop
- Optionally convert anticipated bad data into errors rather than letting a panic occur
Example fix
// before
v := s.data[key].(string) // panics on wrong type
// after
v, ok := s.data[key].(string)
if !ok {
return fmt.Errorf("unexpected type for key %q", key)
} Defensive patterns
Strategy: try-catch
Validate before calling
if src == nil { return errors.New("source required") }
if err := preflight(src); err != nil { return err } Type guard
func isSourcePanic(err error) bool {
return errors.Is(err, errStreamPanic)
} Try / catch
termErr := readStream(src, items, quit)
if err := <-termErr; err != nil {
if errors.Is(err, errStreamPanic) {
log.Printf("source panic recovered: %v", err) // '%v' holds the panic value
return
}
log.Printf("stream ended with error: %v", err)
} Prevention
- Treat the error's %v suffix as the panic value and log it with stack context
- Use safe accessors (comma-ok assertions, nil checks) inside source loops
- Convert expected bad-data cases into returned errors, not panics
- Fuzz/source-test Streamer implementations against unexpected inputs
When it happens
Trigger: Any runtime panic inside the Streamer's produce loop (nil dereference, slice out of range, failed type assertion, explicit panic()) while readStream drains the source; the deferred recover captures p and wraps it.
Common situations: Streaming handlers over live/untrusted data where a nil field or unexpected type triggers a panic mid-stream; concurrent modification of the underlying data during streaming.
Related errors
- stream source panicked
- errors.Wrap(e.Err, e.Message).Error()
- failed to generate signed URL for %q: %w
- invalid rate limiter config: %v
- stream source is nil
AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01).
Data as JSON: /api/errors/6678133a60b74704.
Report an issue: GitHub.