{"record":{"id":"a6eae34fde4cf5ad","repo":"gofiber/fiber","slug":"client-panic-v","errorCode":null,"errorMessage":"client panic: %v","messagePattern":"client panic: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/core.go","lineNumber":77,"sourceCode":"\t\tdefer releaseResponseChan(respChan)\n\n\t\treqv := fasthttp.AcquireRequest()\n\t\tdefer fasthttp.ReleaseRequest(reqv)\n\n\t\trespv := fasthttp.AcquireResponse()\n\t\tdefer func() {\n\t\t\tif respv != nil {\n\t\t\t\tfasthttp.ReleaseResponse(respv)\n\t\t\t}\n\t\t}()\n\n\t\tvar resp *Response\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tif resp != nil {\n\t\t\t\t\tReleaseResponse(resp)\n\t\t\t\t}\n\t\t\t\terrChan <- fmt.Errorf(\"client panic: %v\", r)\n\t\t\t}\n\t\t}()\n\n\t\tc.req.RawRequest.CopyTo(reqv)\n\t\tif bodyStream := c.req.RawRequest.BodyStream(); bodyStream != nil {\n\t\t\treqv.SetBodyStream(bodyStream, c.req.RawRequest.Header.ContentLength())\n\t\t}\n\n\t\tvar err error\n\t\tif cfg != nil {\n\t\t\t// Use an exponential backoff retry strategy.\n\t\t\terr = retry.NewExponentialBackoff(*cfg).Retry(func() error {\n\t\t\t\tif c.req.maxRedirects > 0 && (string(reqv.Header.Method()) == fiber.MethodGet || string(reqv.Header.Method()) == fiber.MethodHead || string(reqv.Header.Method()) == fiber.MethodQuery) {\n\t\t\t\t\treturn c.client.DoRedirects(reqv, respv, c.req.maxRedirects)\n\t\t\t\t}\n\t\t\t\treturn c.client.Do(reqv, respv)\n\t\t\t})\n\t\t} else {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/client/core.go#L59-L95","documentation":"execFunc runs the HTTP exchange in a goroutine and recovers any panic, converting it into this error so a crash inside fasthttp (or a request hook) surfaces as a normal error instead of killing the process. The %v renders the recovered value, which may be a runtime.Error or any panic payload.","triggerScenarios":"A nil-pointer dereference or index-out-of-range inside fasthttp's Do/DoRedirects, a buggy request hook (e.g. a custom dialer or header callback) panicking, or a bodyStream whose Read panics. Also reachable if c.req.RawRequest is malformed in a way fasthttp does not guard.","commonSituations":"Custom DialFunc, ProxyDialer, or StreamingBody callback that panics; concurrent use of a non-thread-safe object shared across requests; a fasthttp version regression.","solutions":["Inspect the recovered payload in logs (enable debug logging) to find the panic origin.","Wrap any custom callbacks (Dial, body readers, hooks) so they cannot panic into fasthttp; recover at their boundary.","Ensure the Client and Request are used according to their lifetime contract (do not reuse a Request across goroutines, do not mutate RawRequest after sending)."],"exampleFix":"// before\nc.SetDial(func(addr string) (net.Conn, error) { return myPool.Get(addr) })\n\n// after\nc.SetDial(func(addr string) (net.Conn, error) {\n    defer func() { _ = recover() }() // keep panics out of fasthttp\n    return myPool.Get(addr)\n})","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := c.Get(url)\nif err != nil && strings.Contains(err.Error(), \"client panic\") {\n    // a hook or fasthttp internals panicked; log payload, do not blindly retry\n    log.Printf(\"recovered panic in request: %v\", err)\n}","preventionTips":["Wrap any custom callback (Dial, body reader, hooks) with recover so panics never enter fasthttp.","Never share a Request across goroutines or mutate RawRequest after dispatch.","Pin fasthttp to a tested version; regressions here are usually upstream."],"tags":["client","panic","recovery","goroutine","fasthttp"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}