crowdsecurity/crowdsec · error
failed to decode response: %w
Error message
failed to decode response: %w
What it means
When the PAPI /permissions endpoint answers with a non-200 status, GetPermissions tries to decode the body as PapiPermCheckError{error}. This error means even that error payload could not be JSON-decoded — the response body wasn't the expected JSON error object (e.g. an HTML page, empty body, or different schema). The real HTTP failure status is then obscured, so only the status code path is known to be bad.
Source
Thrown at pkg/apiserver/papi.go:184
req, err := http.NewRequestWithContext(ctx, http.MethodGet, papiCheckURL, http.NoBody)
if err != nil {
return PapiPermCheckSuccess{}, fmt.Errorf("failed to create request: %w", err)
}
resp, err := httpClient.Do(req)
if err != nil {
return PapiPermCheckSuccess{}, fmt.Errorf("failed to get response: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
errResp := PapiPermCheckError{}
err = json.NewDecoder(resp.Body).Decode(&errResp)
if err != nil {
return PapiPermCheckSuccess{}, fmt.Errorf("failed to decode response: %w", err)
}
return PapiPermCheckSuccess{}, fmt.Errorf("unable to query PAPI : %s (%d)", errResp.Error, resp.StatusCode)
}
respBody := PapiPermCheckSuccess{}
err = json.NewDecoder(resp.Body).Decode(&respBody)
if err != nil {
return PapiPermCheckSuccess{}, fmt.Errorf("failed to decode response: %w", err)
}
return respBody, nil
}
func reverse(s []longpollclient.Event) []longpollclient.Event {
a := make([]longpollclient.Event, len(s))
copy(a, s)View on GitHub (pinned to 909b515798)
Solutions
- Log/inspect the raw response body and status code at the moment of failure (curl the same URL with the machine credentials).
- If it's a CDN/502/503, treat it as a transient CAPI outage and retry later; PAPI permission checks re-run periodically.
- Remove any misbehaving proxy between the host and api.crowdsec.net.
- Ensure papi_url points to the real PAPI endpoint; upgrade crowdsec if the API's error schema changed.
Defensive patterns
Strategy: retry
Try / catch
perms, err := papi.GetPermissions(ctx)
if err != nil {
if strings.Contains(err.Error(), "failed to decode response") || strings.Contains(err.Error(), "unable to query PAPI") {
// non-200 from PAPI with unexpected body: transient upstream issue — retry with backoff
return retryWithBackoff(ctx)
}
} Prevention
- Ensure no intercepting proxy/CDN sits between the host and api.crowdsec.net.
- Handle expected error statuses (4xx/5xx) gracefully instead of assuming JSON error bodies.
- Subscribe to CrowdSec status updates to correlate failures with CAPI incidents.
- Upgrade crowdsec when the PAPI error schema changes so both sides speak the same shape.
When it happens
Trigger: resp.StatusCode != http.StatusOK and json.NewDecoder(resp.Body).Decode(&errResp) fails inside GetPermissions — the endpoint returned e.g. a 502/503 HTML gateway error page, an empty body, or rate-limit text instead of {"error": ...}.
Common situations: CAPI outage or maintenance returning gateway HTML; a proxy/CDN interposing an error page; severe rate limiting returning plain-text responses; pointing papi_url at a non-PAPI host that answers 404 HTML.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unable to query PAPI : %s (%d)
- no header in message, skipping
- no source user in header message, skipping
- unauthorized
- request quota exceeded, please reduce your request rate
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/1623953ae2dddb10.
Report an issue: GitHub.