{"record":{"id":"9945fca12cdcea3f","repo":"crowdsecurity/crowdsec","slug":"while-performing-request-w","errorCode":null,"errorMessage":"while performing request: %w","messagePattern":"while performing request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/apiclient/decisions_sync_service.go","lineNumber":28,"sourceCode":"\t\"github.com/crowdsecurity/crowdsec/pkg/models\"\n)\n\ntype DecisionDeleteService service\n\n// DecisionDeleteService purposely reuses AddSignalsRequestItemDecisions model\nfunc (d *DecisionDeleteService) Add(ctx context.Context, deletedDecisions *models.DecisionsDeleteRequest) (interface{}, *Response, error) {\n\tu := fmt.Sprintf(\"%s/decisions/delete\", d.client.URLPrefix)\n\n\treq, err := d.client.PrepareRequest(ctx, http.MethodPost, u, &deletedDecisions)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"while building request: %w\", err)\n\t}\n\n\tvar response interface{}\n\n\tresp, err := d.client.Do(ctx, req, &response)\n\tif err != nil {\n\t\treturn nil, resp, fmt.Errorf(\"while performing request: %w\", err)\n\t}\n\n\tif resp.Response.StatusCode != http.StatusOK {\n\t\tlog.Warnf(\"Decisions delete response: http %s\", resp.Response.Status)\n\t} else {\n\t\tlog.Debugf(\"Decisions delete response: http %s\", resp.Response.Status)\n\t}\n\n\treturn &response, resp, nil\n}\n","sourceCodeStart":10,"sourceCodeEnd":39,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiclient/decisions_sync_service.go#L10-L39","documentation":"DecisionDeleteService.Add wraps any failure of client.Do — the actual HTTP round trip for POST /decisions/delete. This covers transport errors (connection refused, DNS, timeout, TLS) as well as response-decoding errors; the returned resp (when non-nil) holds the server's status for further inspection. The method separately logs non-200 statuses after this, so this wrapper is the network/decoding failure path.","triggerScenarios":"d.client.Do(ctx, req, &response) returns an error: LAPI unreachable, connection reset, context canceled/deadline exceeded, or the response body could not be decoded into the response model.","commonSituations":"LAPI down or restarted while a bouncer deletes decisions; network interruption; request timeout under load; TLS certificate problems; context canceled because the caller's deadline expired mid-call.","solutions":["Check LAPI availability and logs at the time of the delete","Inspect the returned *Response for a status code to distinguish HTTP-level vs transport-level failure","Increase the context deadline / add retry with backoff for transient network errors","Verify TLS/CA configuration between client and LAPI"],"exampleFix":"// before\nresp, err := d.client.Do(ctx, req, &response)\nif err != nil {\n    return nil, resp, fmt.Errorf(\"while performing request: %w\", err)\n}\n// after (caller-side retry on transient errors)\nvar lastErr error\nfor i := 0; i < 3; i++ {\n    resp, err := d.client.Do(ctx, req, &response)\n    if err == nil {\n        break\n    }\n    lastErr = fmt.Errorf(\"while performing request: %w\", err)\n    time.Sleep(time.Duration(1<<i) * time.Second)\n}\nif lastErr != nil {\n    return nil, nil, lastErr\n}","handlingStrategy":"retry","validationCode":"// pre-check LAPI reachability before deleting decisions\nresp, err := http.Get(client.BaseURL.String() + \"health\")\nif err != nil {\n    return fmt.Errorf(\"LAPI unreachable, skipping decisions delete: %w\", err)\n}\nresp.Body.Close()","typeGuard":"func isTransient(err error) bool {\n    return errors.Is(err, context.DeadlineExceeded) ||\n        errors.Is(err, syscall.ECONNRESET) ||\n        errors.Is(err, syscall.ECONNREFUSED) ||\n        errors.Is(err, io.ErrUnexpectedEOF)\n}","tryCatchPattern":"_, resp, err := client.DecisionsDelete.Add(ctx, req)\nif err != nil {\n    if isTransient(err) {\n        return retryWithBackoff(ctx, 3, func() error { _, _, err := client.DecisionsDelete.Add(ctx, req); return err })\n    }\n    if resp != nil && resp.Response != nil {\n        return fmt.Errorf(\"decisions delete got http %s\", resp.Response.Status)\n    }\n    return err\n}","preventionTips":["Set generous context deadlines for LAPI calls under load","Retry transient network errors with exponential backoff","Monitor LAPI availability and alert on restarts","Handle context cancellation explicitly in long-running bouncers"],"tags":["network","lapi","http-request"],"backgroundTag":"api-request-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}