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:
			return

View on GitHub (pinned to 187eb24962)

Solutions

  1. Read the '%v' suffix of the error to see the actual panic value and pinpoint the bug
  2. Fix the panicking code in your Streamer implementation
  3. Add nil/type checks before touching data inside the source loop
  4. 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

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


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/6678133a60b74704. Report an issue: GitHub.