crowdsecurity/crowdsec · error

errUnauthorized

errUnauthorized

Error message

user is not authorized to use PAPI

What it means

Sentinel error of the PAPI long-poll client. When the server replies to a long-poll request with HTTP 401 (or another unauthorized status handled as such), poll/pollEvents return errUnauthorized, which the main loop treats as fatal: it logs 'unauthorized, stopping polling' and kills the tidpool task rather than retrying.

Source

Thrown at pkg/longpollclient/client.go:53

}

type Event struct {
	Timestamp int64     `json:"timestamp"`
	Category  string    `json:"category"`
	Data      string    `json:"data"`
	ID        uuid.UUID `json:"id"`
	RequestId string
}

type pollResponse struct {
	Events []Event `json:"events"`
	// Set for timeout responses
	Timestamp int64 `json:"timestamp"`
	// API error responses could have an informative error here. Empty on success.
	ErrorMessage string `json:"error"`
}

var errUnauthorized = errors.New("user is not authorized to use PAPI")

const timeoutMessage = "no events before timeout"

func (c *LongPollClient) doQuery(ctx context.Context) (*http.Response, error) {
	logger := c.logger.WithField("method", "doQuery")
	query := c.url.Query()
	query.Set("since_time", fmt.Sprintf("%d", c.since))
	query.Set("timeout", c.timeout)
	c.url.RawQuery = query.Encode()

	logger.Debugf("Query parameters: %s", c.url.RawQuery)

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url.String(), http.NoBody)
	if err != nil {
		logger.Errorf("failed to create request: %s", err)
		return nil, err
	}
	req.Header.Set("Accept", "application/json")

View on GitHub (pinned to 909b515798)

Solutions

  1. Refresh the PAPI credentials (re-register or update the API key) and restart the poller.
  2. Verify the machine/account is actually authorized for PAPI in the CrowdSec console.
  3. Check system machine-id / registration mismatch: 'cscli capi register' or 'cscli console enroll' as appropriate.
Defensive patterns

Strategy: try-catch

Validate before calling

// before starting: validate the PAPI token works
resp, err := http.Get(papiURL + "/whoami")
if resp != nil && resp.StatusCode == 401 {
    return errors.New("PAPI credentials invalid, re-register before polling")
}

Try / catch

if err := poller.Run(ctx); err != nil {
    if errors.Is(err, longpollclient.ErrUnauthorizedEquivalent) {
        // refresh credentials and restart, do not hot-retry
        refreshCredentials(); poller.Run(ctx)
    }
}

Prevention

When it happens

Trigger: doQuery receives a non-OK response with the unauthorized status path taken at client.go:104 — typically an expired/revoked PAPI (Central API) token, a machine registered without PAPI access, or wrong credentials configured for the long-poll client.

Common situations: API key rotated or deleted on the CAPI side while the poller keeps running, crowdsec agent re-registered (new credentials pushed) but client using old ones, account lacking PAPI entitlement.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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