crowdsecurity/crowdsec · error
error decoding poll response: %v
Error message
error decoding poll response: %v
What it means
pkg/longpollclient's poll() streams a long-poll HTTP response and decodes each JSON chunk into the poll response struct. If json.Decoder.Decode fails for any reason other than a clean io.EOF (server-closed connection), it wraps the decoder error with this message and aborts polling. It signals a malformed or truncated response body rather than an application-level error from the server.
Source
Thrown at pkg/longpollclient/client.go:129
for {
select {
case <-c.t.Dying():
logger.Debugf("dying")
close(c.c)
return nil
case <-ctx.Done():
logger.Debugf("context canceled")
close(c.c)
return ctx.Err()
default:
var pollResp pollResponse
err = decoder.Decode(&pollResp)
if err != nil {
if errors.Is(err, io.EOF) {
logger.Debugf("server closed connection")
return nil
}
return fmt.Errorf("error decoding poll response: %v", err)
}
logger.Tracef("got response: %+v", pollResp)
if pollResp.ErrorMessage != "" {
if pollResp.ErrorMessage == timeoutMessage {
logger.Debugf("got timeout message")
return nil
}
return fmt.Errorf("longpoll API error message: %s", pollResp.ErrorMessage)
}
if len(pollResp.Events) > 0 {
logger.Debugf("got %d events", len(pollResp.Events))
for _, event := range pollResp.Events {
event.RequestId = requestId
c.c <- event
if event.Timestamp > c.since {View on GitHub (pinned to 909b515798)
Solutions
- Inspect the wrapped error (%v) to see if it is an unexpected-EOF/truncated-body issue and check any proxy between client and LAPI for idle/stream timeouts.
- Verify the LAPI endpoint/version is compatible with the client and returns application/json poll chunks.
- Check network stability (TLS handshake failures, dropped connections) and rely on pollEvents' retry loop to re-establish the stream.
- Log the raw failing payload at debug level to confirm whether the server or an intermediary corrupted the response.
Example fix
// before: retry blindly on every poll error
for {
if err := client.Poll(); err != nil { continue }
}
// after: log the decode error and back off before retrying
if err := client.Poll(); err != nil {
if strings.Contains(err.Error(), "error decoding poll response") {
logger.Errorf("malformed poll response: %v", err)
time.Sleep(retryDelay)
continue
}
} Defensive patterns
Strategy: retry
Try / catch
// caller pattern: treat as transient, log and let pollEvents retry
if err := client.Poll(); err != nil {
if strings.Contains(err.Error(), "error decoding poll response") {
logger.Warnf("poll stream decode failed, will retry: %v", err)
time.Sleep(retryBackoff)
continue
}
return err
} Prevention
- Keep proxies/LBs between client and LAPI configured with long idle timeouts to avoid mid-stream truncation.
- Keep client and LAPI versions compatible so the poll payload shape matches.
- Monitor network stability and reconnect logic in long-running deployments.
When it happens
Trigger: poll() (called by pollEvents) receives a chunk whose payload is not valid JSON, is cut off mid-stream, or has an encoding issue; errors.Is(err, io.EOF) is false so it is not treated as a normal timeout/close.
Common situations: A reverse proxy or load balancer truncates the long-poll stream; the crowdsec LAPI returns an HTML error page or empty body instead of JSON; network interruption mid-stream; incompatible LAPI version emitting an unexpected payload shape.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- longpoll API error message: %s
- loki channel closed
- errUnauthorized
- while reading %s/%s: %w
- cannot subscribe to shard: %w
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/40d005fe00138365.
Report an issue: GitHub.