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

  1. 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.
  2. Verify the LAPI endpoint/version is compatible with the client and returns application/json poll chunks.
  3. Check network stability (TLS handshake failures, dropped connections) and rely on pollEvents' retry loop to re-establish the stream.
  4. 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

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


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