{"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":"critical","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/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/client/core.go#L59-L95","documentation":"Produced in core.execFunc (client/core.go:77) by a recover() inside the goroutine that drives fasthttp's client.Do/DoRedirects. Any panic raised while copying the request, performing the dial, or building the Response is captured and delivered to the caller as a normal error on the errChan, so a panic never crashes the whole process. Because the panic value is formatted with %v, the message is whatever r the panic carried.","triggerScenarios":"A nil-pointer dereference or index-out-of-range inside fasthttp while executing the request; a panic in a user-supplied retry callback body, a hook, or a custom marshaler invoked during request building; concurrent misuse of a Request/Response that fasthttp does not tolerate (e.g. reusing an acquired request from two goroutines).","commonSituations":"Sharing a single *Request across goroutines; a bodyStream that panics on read; a custom JSON/XML marshaler that panics on an unsupported type; a fasthttp version regression that panics on a malformed header value; retry logic whose callback closure captures a nil client.","solutions":["Inspect the formatted panic value (often a runtime error string) to find the originating stack — enable a debugger or wrap the call to log r.","Ensure each Request is used by exactly one goroutine and not reused after being sent.","Validate/replace custom marshalers and hooks so they return errors instead of panicking.","Upgrade fasthttp/Fiber to pick up panic fixes if the trace points into library internals."],"exampleFix":"// before — request shared across goroutines, fasthttp panics\nfor _, u := range urls {\n    go func() { resp, _ := core.execute(ctx, client, sharedReq); _ = resp }()\n}\n\n// after — each goroutine gets its own acquired request\nfor _, u := range urls {\n    go func(u string) {\n        req := AcquireRequest()\n        defer ReleaseRequest(req)\n        req.SetURL(u)\n        resp, err := core.execute(ctx, client, req)\n        if err != nil { log.Printf(\"request failed/panicked: %v\", err) }\n    }(u)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := core.execute(ctx, client, req)\nif err != nil {\n    if strings.Contains(err.Error(), \"client panic\") {\n        // a panic was recovered inside the client; log r and fail/restart\n        log.Printf(\"recovered client panic: %v\", err)\n    }\n    return err\n}","preventionTips":["Never share a single *Request across goroutines; acquire a fresh one per call.","Ensure custom marshalers and hooks return errors rather than panicking.","Keep Fiber and fasthttp updated to benefit from internal panic fixes.","Add logging for the recovered panic value to speed root-cause analysis."],"tags":["client","panic","recovery","concurrency"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}