{"record":{"id":"5c7d397eff1b4a79","repo":"crowdsecurity/crowdsec","slug":"polling-papi-message-format-is-not-compatible-v","errorCode":null,"errorMessage":"polling papi message format is not compatible: %+v: %w","messagePattern":"polling papi message format is not compatible: %\\+v: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/apiserver/papi.go","lineNumber":135,"sourceCode":"\t\tpullTomb:      tomb.Tomb{},\n\t\tsyncTomb:      tomb.Tomb{},\n\t\tapiClient:     apic.apiClient,\n\t\tapic:          apic,\n\t\tconsoleConfig: consoleConfig,\n\t\tLogger:        logger.WithFields(log.Fields{\"interval\": SyncInterval.Seconds(), \"source\": \"papi\"}),\n\t\tstopChan:      make(chan struct{}),\n\t}\n\n\treturn papi, nil\n}\n\nfunc (p *Papi) handleEvent(ctx context.Context, event longpollclient.Event, sync bool) error {\n\tlogger := p.Logger.WithField(\"request-id\", event.RequestId)\n\tlogger.Debugf(\"message received: %+v\", event.Data)\n\n\tmessage := &Message{}\n\tif err := json.Unmarshal([]byte(event.Data), message); err != nil {\n\t\treturn fmt.Errorf(\"polling papi message format is not compatible: %+v: %w\", event.Data, err)\n\t}\n\n\tif message.Header == nil {\n\t\treturn errors.New(\"no header in message, skipping\")\n\t}\n\n\tif message.Header.Source == nil {\n\t\treturn errors.New(\"no source user in header message, skipping\")\n\t}\n\n\toperationFunc, ok := operationMap[message.Header.OperationType]\n\tif !ok {\n\t\treturn fmt.Errorf(\"operation '%s' unknown, continue\", message.Header.OperationType)\n\t}\n\n\tmetrics.PapiOrdersReceived.WithLabelValues(message.Header.OperationType, message.Header.OperationCmd).Inc()\n\n\tlogger.Debugf(\"Calling operation '%s'\", message.Header.OperationType)","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiserver/papi.go#L117-L153","documentation":"The PAPI long-poll loop received an event whose payload could not be JSON-unmarshaled into the internal Message structure, so the order (decision/alert/management change) is dropped. The server logs and skips this message rather than crashing, since subsequent poll iterations will continue. This guards against incompatible or truncated payloads from the polling endpoint.","triggerScenarios":"json.Unmarshal([]byte(event.Data), message) fails inside handleEvent — the long-poll response body is not valid JSON or does not match the Message schema (missing/renamed fields, HTML error page instead of JSON, wrong endpoint version).","commonSituations":"A proxy/Captive portal or the CDN returning an HTML error page on the poll URL; CAPI API version drift where the message schema changed; a misconfigured papi_url pointing at a non-PAPI endpoint; corrupted gzip/proxy encoding mangling the body.","solutions":["Look at the logged '%+v' payload in the error to see what was actually received.","Verify api.client.papi_url points at the official CAPI PAPI endpoint (…/v1/decisions/stream/poll path is set automatically by NewPAPI).","Check for intercepting proxies / corporate TLS inspection altering the response; bypass or fix them.","Upgrade crowdsec — a schema change from CAPI usually requires a matching client release.","Restart crowdsec to re-authenticate and get a clean session if the endpoint is returning error pages."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"var probe map[string]any\nif err := json.Unmarshal(raw, &probe); err != nil {\n    // payload isn't JSON at all — likely a proxy/error page; alert before feeding to consumers\n}","typeGuard":null,"tryCatchPattern":"if err := p.handleEvent(ctx, event, sync); err != nil {\n    var ute *json.UnmarshalTypeError\n    if errors.As(err, &ute) {\n        logger.WithField(\"data\", event.Data).Warn(\"PAPI message schema mismatch — check crowdsec/CAPI versions\")\n    }\n    // continue polling; single bad message must not kill the loop\n    continue\n}","preventionTips":["Keep crowdsec up to date so the Message schema matches current CAPI.","Ensure no TLS-intercepting proxy or captive portal rewrites responses on the CAPI endpoint.","Alert on repeated occurrences — one bad message is noise, a stream of them is a version/proxy problem.","Log the raw event payload at debug level during incidents to capture the incompatible format."],"tags":["papi","json","polling","network"],"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"}