valyala/fasthttp · error · ErrBodyStreamWritePanic
panic while writing body stream: %+v
Error message
panic while writing body stream: %+v
What it means
When a response body is a stream (BodyStream set), fasthttp writes it inside a recover guard; if the stream's Write panics, the panic is converted into an ErrBodyStreamWritePanic wrapping this message plus the recovered value. This prevents a user-supplied stream panic from crashing the server/client goroutine.
Source
Thrown at http.go:2350
}
}
errc := req.closeBodyStream()
if err == nil {
err = errc
}
return err
}
// ErrBodyStreamWritePanic is returned when panic happens during writing body stream.
type ErrBodyStreamWritePanic struct {
error
}
func (resp *Response) writeBodyStream(w *bufio.Writer, sendBody bool) (err error) {
defer func() {
if r := recover(); r != nil {
err = &ErrBodyStreamWritePanic{
error: fmt.Errorf("panic while writing body stream: %+v", r),
}
}
}()
contentLength := resp.Header.ContentLength()
if contentLength < 0 {
lrSize := limitedReaderSize(resp.bodyStream)
if lrSize >= 0 {
contentLength = int(lrSize)
if int64(contentLength) != lrSize {
contentLength = -1
}
if contentLength >= 0 {
resp.Header.SetContentLength(contentLength)
}
}
}
if contentLength >= 0 {View on GitHub (pinned to c96f600972)
Solutions
- Fix the panicking code in your BodyStream reader/writer (inspect the %+v value for the panic message and stack hint).
- Recover inside your own stream implementation and return an error from Read instead of panicking.
- Verify the size hint passed to SetBodyStream matches reality to avoid mismatched writes.
- Test streams with concurrent Close/Read to surface races (go test -race).
Example fix
// before
resp.SetBodyStream(riskyReader, -1) // riskyReader panics on EOF
// after
resp.SetBodyStream(safeReader{r: riskyReader}, -1)
type safeReader struct{ r io.Reader }
func (s safeReader) Read(p []byte) (int, error) {
defer func() { if r := recover(); r != nil { /* log */ } }()
return s.r.Read(p)
} Defensive patterns
Strategy: try-catch
Validate before calling
if r == nil { return errors.New("nil body stream reader") } Type guard
func nonPanicReader(r io.Reader) io.Reader {
return panicSafeReader{r}
}
type panicSafeReader struct{ io.Reader }
func (p panicSafeReader) Read(b []byte) (n int, err error) {
defer func() {
if rec := recover(); rec != nil {
err = fmt.Errorf("stream read panic: %v", rec)
}
}()
return p.Reader.Read(b)
} Try / catch
// fasthttp already converts panics; handle the typed error
if err := ctx.Write(respBody); err != nil {
var pe *fasthttp.ErrBodyStreamWritePanic
if errors.As(err, &pe) {
log.Printf("body stream panicked: %v", pe.Unwrap())
}
return err
} Prevention
- Never let stream readers dereference nil or index out of range
- Guard shared state accessed inside stream Read (data races)
- Return errors from Read instead of panicking
- Test streams under -race and with early client disconnects
When it happens
Trigger: Setting resp.SetBodyStream(reader, size) (server or client) where the reader's Read or the wrapper Write panics — nil map access, index out of range, or a nil reader dereference inside custom streaming code.
Common situations: Streaming from a generator function that panics on exhaustion; passing a reader that panics when read after Close; data races on shared state accessed inside the stream's Read.
Related errors
- copied %d bytes from body stream instead of %d bytes
- fasthttp: cannot follow a body-preserving redirect for a req
- fasthttp: need more data: cannot find trailing lf
- invalid WriteHeader code %v
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/7c5022f0d4fd09fd.
Report an issue: GitHub.