{"record":{"id":"6678133a60b74704","repo":"gofr-dev/gofr","slug":"w-v-667813","errorCode":null,"errorMessage":"%w: %v","messagePattern":"%w: %v","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/http/stream.go","lineNumber":66,"sourceCode":"\tquit := make(chan struct{})\n\ttermErr := make(chan error, 1) // the source's terminal error, delivered once at end\n\n\tdefer close(quit)\n\n\tgo readStream(s.Source, items, quit, termErr)\n\n\tr.pump(rc, s, items, termErr)\n}\n\n// readStream pulls from the source into items until the source is done or the consumer quits. A\n// panic in the source is recovered into an error frame instead of crashing the server. The\n// terminal error is sent before items is closed, so pump reads it safely after draining.\nfunc readStream(src resTypes.Streamer, items chan<- any, quit <-chan struct{}, termErr chan<- error) {\n\tvar err error\n\n\tdefer func() {\n\t\tif p := recover(); p != nil {\n\t\t\terr = fmt.Errorf(\"%w: %v\", errStreamPanic, p)\n\t\t}\n\n\t\ttermErr <- err\n\n\t\tclose(items)\n\t}()\n\n\tfor {\n\t\tv, ok := src.Next()\n\t\tif !ok {\n\t\t\terr = src.Err()\n\t\t\treturn\n\t\t}\n\n\t\tselect {\n\t\tcase items <- v:\n\t\tcase <-quit:\n\t\t\treturn","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/http/stream.go#L48-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nv := s.data[key].(string) // panics on wrong type\n// after\nv, ok := s.data[key].(string)\nif !ok {\n    return fmt.Errorf(\"unexpected type for key %q\", key)\n}","handlingStrategy":"try-catch","validationCode":"if src == nil { return errors.New(\"source required\") }\nif err := preflight(src); err != nil { return err }","typeGuard":"func isSourcePanic(err error) bool {\n    return errors.Is(err, errStreamPanic)\n}","tryCatchPattern":"termErr := readStream(src, items, quit)\nif err := <-termErr; err != nil {\n    if errors.Is(err, errStreamPanic) {\n        log.Printf(\"source panic recovered: %v\", err) // '%v' holds the panic value\n        return\n    }\n    log.Printf(\"stream ended with error: %v\", err)\n}","preventionTips":["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"],"tags":["streaming","panic","error-wrapping"],"backgroundTag":"stream-source-panic","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}