crowdsecurity/crowdsec · warning

polling papi message format is not compatible: %+v: %w

Error message

polling papi message format is not compatible: %+v: %w

What it means

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.

Source

Thrown at pkg/apiserver/papi.go:135

		pullTomb:      tomb.Tomb{},
		syncTomb:      tomb.Tomb{},
		apiClient:     apic.apiClient,
		apic:          apic,
		consoleConfig: consoleConfig,
		Logger:        logger.WithFields(log.Fields{"interval": SyncInterval.Seconds(), "source": "papi"}),
		stopChan:      make(chan struct{}),
	}

	return papi, nil
}

func (p *Papi) handleEvent(ctx context.Context, event longpollclient.Event, sync bool) error {
	logger := p.Logger.WithField("request-id", event.RequestId)
	logger.Debugf("message received: %+v", event.Data)

	message := &Message{}
	if err := json.Unmarshal([]byte(event.Data), message); err != nil {
		return fmt.Errorf("polling papi message format is not compatible: %+v: %w", event.Data, err)
	}

	if message.Header == nil {
		return errors.New("no header in message, skipping")
	}

	if message.Header.Source == nil {
		return errors.New("no source user in header message, skipping")
	}

	operationFunc, ok := operationMap[message.Header.OperationType]
	if !ok {
		return fmt.Errorf("operation '%s' unknown, continue", message.Header.OperationType)
	}

	metrics.PapiOrdersReceived.WithLabelValues(message.Header.OperationType, message.Header.OperationCmd).Inc()

	logger.Debugf("Calling operation '%s'", message.Header.OperationType)

View on GitHub (pinned to 909b515798)

Solutions

  1. Look at the logged '%+v' payload in the error to see what was actually received.
  2. Verify api.client.papi_url points at the official CAPI PAPI endpoint (…/v1/decisions/stream/poll path is set automatically by NewPAPI).
  3. Check for intercepting proxies / corporate TLS inspection altering the response; bypass or fix them.
  4. Upgrade crowdsec — a schema change from CAPI usually requires a matching client release.
  5. Restart crowdsec to re-authenticate and get a clean session if the endpoint is returning error pages.
Defensive patterns

Strategy: try-catch

Validate before calling

var probe map[string]any
if err := json.Unmarshal(raw, &probe); err != nil {
    // payload isn't JSON at all — likely a proxy/error page; alert before feeding to consumers
}

Try / catch

if err := p.handleEvent(ctx, event, sync); err != nil {
    var ute *json.UnmarshalTypeError
    if errors.As(err, &ute) {
        logger.WithField("data", event.Data).Warn("PAPI message schema mismatch — check crowdsec/CAPI versions")
    }
    // continue polling; single bad message must not kill the loop
    continue
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

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/5c7d397eff1b4a79. Report an issue: GitHub.