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
- Refresh the PAPI credentials (re-register or update the API key) and restart the poller.
- Verify the machine/account is actually authorized for PAPI in the CrowdSec console.
- 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
- Rotate and propagate CAPI/PAPI credentials before the old ones expire
- Alert on the 'unauthorized, stopping polling' log line
- Re-run registration after re-enrolling machines or consoles
- Avoid sharing credentials across re-registered agents
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.