crowdsecurity/crowdsec · error

while performing request: %w

Error message

while performing request: %w

What it means

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.

Source

Thrown at pkg/apiclient/decisions_sync_service.go:28

	"github.com/crowdsecurity/crowdsec/pkg/models"
)

type DecisionDeleteService service

// DecisionDeleteService purposely reuses AddSignalsRequestItemDecisions model
func (d *DecisionDeleteService) Add(ctx context.Context, deletedDecisions *models.DecisionsDeleteRequest) (interface{}, *Response, error) {
	u := fmt.Sprintf("%s/decisions/delete", d.client.URLPrefix)

	req, err := d.client.PrepareRequest(ctx, http.MethodPost, u, &deletedDecisions)
	if err != nil {
		return nil, nil, fmt.Errorf("while building request: %w", err)
	}

	var response interface{}

	resp, err := d.client.Do(ctx, req, &response)
	if err != nil {
		return nil, resp, fmt.Errorf("while performing request: %w", err)
	}

	if resp.Response.StatusCode != http.StatusOK {
		log.Warnf("Decisions delete response: http %s", resp.Response.Status)
	} else {
		log.Debugf("Decisions delete response: http %s", resp.Response.Status)
	}

	return &response, resp, nil
}

View on GitHub (pinned to 909b515798)

Solutions

  1. Check LAPI availability and logs at the time of the delete
  2. Inspect the returned *Response for a status code to distinguish HTTP-level vs transport-level failure
  3. Increase the context deadline / add retry with backoff for transient network errors
  4. Verify TLS/CA configuration between client and LAPI

Example fix

// before
resp, err := d.client.Do(ctx, req, &response)
if err != nil {
    return nil, resp, fmt.Errorf("while performing request: %w", err)
}
// after (caller-side retry on transient errors)
var lastErr error
for i := 0; i < 3; i++ {
    resp, err := d.client.Do(ctx, req, &response)
    if err == nil {
        break
    }
    lastErr = fmt.Errorf("while performing request: %w", err)
    time.Sleep(time.Duration(1<<i) * time.Second)
}
if lastErr != nil {
    return nil, nil, lastErr
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-check LAPI reachability before deleting decisions
resp, err := http.Get(client.BaseURL.String() + "health")
if err != nil {
    return fmt.Errorf("LAPI unreachable, skipping decisions delete: %w", err)
}
resp.Body.Close()

Type guard

func isTransient(err error) bool {
    return errors.Is(err, context.DeadlineExceeded) ||
        errors.Is(err, syscall.ECONNRESET) ||
        errors.Is(err, syscall.ECONNREFUSED) ||
        errors.Is(err, io.ErrUnexpectedEOF)
}

Try / catch

_, resp, err := client.DecisionsDelete.Add(ctx, req)
if err != nil {
    if isTransient(err) {
        return retryWithBackoff(ctx, 3, func() error { _, _, err := client.DecisionsDelete.Add(ctx, req); return err })
    }
    if resp != nil && resp.Response != nil {
        return fmt.Errorf("decisions delete got http %s", resp.Response.Status)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/9945fca12cdcea3f. Report an issue: GitHub.