{"record":{"id":"d8589f36ee2cc44a","repo":"crowdsecurity/crowdsec","slug":"errunauthorized","errorCode":"errUnauthorized","errorMessage":"user is not authorized to use PAPI","messagePattern":"user is not authorized to use PAPI","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/longpollclient/client.go","lineNumber":53,"sourceCode":"}\n\ntype Event struct {\n\tTimestamp int64     `json:\"timestamp\"`\n\tCategory  string    `json:\"category\"`\n\tData      string    `json:\"data\"`\n\tID        uuid.UUID `json:\"id\"`\n\tRequestId string\n}\n\ntype pollResponse struct {\n\tEvents []Event `json:\"events\"`\n\t// Set for timeout responses\n\tTimestamp int64 `json:\"timestamp\"`\n\t// API error responses could have an informative error here. Empty on success.\n\tErrorMessage string `json:\"error\"`\n}\n\nvar errUnauthorized = errors.New(\"user is not authorized to use PAPI\")\n\nconst timeoutMessage = \"no events before timeout\"\n\nfunc (c *LongPollClient) doQuery(ctx context.Context) (*http.Response, error) {\n\tlogger := c.logger.WithField(\"method\", \"doQuery\")\n\tquery := c.url.Query()\n\tquery.Set(\"since_time\", fmt.Sprintf(\"%d\", c.since))\n\tquery.Set(\"timeout\", c.timeout)\n\tc.url.RawQuery = query.Encode()\n\n\tlogger.Debugf(\"Query parameters: %s\", c.url.RawQuery)\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url.String(), http.NoBody)\n\tif err != nil {\n\t\tlogger.Errorf(\"failed to create request: %s\", err)\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Accept\", \"application/json\")","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/longpollclient/client.go#L35-L71","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before starting: validate the PAPI token works\nresp, err := http.Get(papiURL + \"/whoami\")\nif resp != nil && resp.StatusCode == 401 {\n    return errors.New(\"PAPI credentials invalid, re-register before polling\")\n}","typeGuard":null,"tryCatchPattern":"if err := poller.Run(ctx); err != nil {\n    if errors.Is(err, longpollclient.ErrUnauthorizedEquivalent) {\n        // refresh credentials and restart, do not hot-retry\n        refreshCredentials(); poller.Run(ctx)\n    }\n}","preventionTips":["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"],"tags":["authentication","http","papi","longpoll"],"backgroundTag":"permission-denied","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"}