{"record":{"id":"40d005fe00138365","repo":"crowdsecurity/crowdsec","slug":"error-decoding-poll-response-v","errorCode":null,"errorMessage":"error decoding poll response: %v","messagePattern":"error decoding poll response: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/longpollclient/client.go","lineNumber":129,"sourceCode":"\tfor {\n\t\tselect {\n\t\tcase <-c.t.Dying():\n\t\t\tlogger.Debugf(\"dying\")\n\t\t\tclose(c.c)\n\t\t\treturn nil\n\t\tcase <-ctx.Done():\n\t\t\tlogger.Debugf(\"context canceled\")\n\t\t\tclose(c.c)\n\t\t\treturn ctx.Err()\n\t\tdefault:\n\t\t\tvar pollResp pollResponse\n\t\t\terr = decoder.Decode(&pollResp)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\t\tlogger.Debugf(\"server closed connection\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"error decoding poll response: %v\", err)\n\t\t\t}\n\n\t\t\tlogger.Tracef(\"got response: %+v\", pollResp)\n\n\t\t\tif pollResp.ErrorMessage != \"\" {\n\t\t\t\tif pollResp.ErrorMessage == timeoutMessage {\n\t\t\t\t\tlogger.Debugf(\"got timeout message\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"longpoll API error message: %s\", pollResp.ErrorMessage)\n\t\t\t}\n\n\t\t\tif len(pollResp.Events) > 0 {\n\t\t\t\tlogger.Debugf(\"got %d events\", len(pollResp.Events))\n\t\t\t\tfor _, event := range pollResp.Events {\n\t\t\t\t\tevent.RequestId = requestId\n\t\t\t\t\tc.c <- event\n\t\t\t\t\tif event.Timestamp > c.since {","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/longpollclient/client.go#L111-L147","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: retry blindly on every poll error\nfor {\n    if err := client.Poll(); err != nil { continue }\n}\n// after: log the decode error and back off before retrying\nif err := client.Poll(); err != nil {\n    if strings.Contains(err.Error(), \"error decoding poll response\") {\n        logger.Errorf(\"malformed poll response: %v\", err)\n        time.Sleep(retryDelay)\n        continue\n    }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// caller pattern: treat as transient, log and let pollEvents retry\nif err := client.Poll(); err != nil {\n    if strings.Contains(err.Error(), \"error decoding poll response\") {\n        logger.Warnf(\"poll stream decode failed, will retry: %v\", err)\n        time.Sleep(retryBackoff)\n        continue\n    }\n    return err\n}","preventionTips":["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."],"tags":["longpoll","json-decode","streaming","crowdsec-lapi"],"backgroundTag":"json-unmarshal-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"}