{"record":{"id":"43a8973280d67bb9","repo":"crowdsecurity/crowdsec","slug":"token-expired","errorCode":null,"errorMessage":"token expired","messagePattern":"token expired","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"pkg/database/token.go","lineNumber":19,"sourceCode":"package database\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v4\"\n\t\"github.com/sirupsen/logrus\"\n)\n\nconst APICTokenKey = \"apic_token\"\n\nvar (\n\tErrTokenNotFound     = errors.New(\"token not found in DB\")\n\tErrTokenParse        = errors.New(\"unable to parse token\")\n\tErrTokenMissingClaim = errors.New(\"token missing required claim\")\n\tErrTokenExpired      = errors.New(\"token expired\")\n)\n\ntype APICToken struct {\n\tRaw       string\n\tExpiresAt time.Time\n}\n\n// LoadAPICToken attempts to retrieve and validate a JWT token from the local database.\n// Errors are returned if the token can't be read, is not valid, expired or has no expiration.\nfunc (c *Client) LoadAPICToken(ctx context.Context, logger logrus.FieldLogger) (APICToken, error) {\n\ttoken, err := c.GetConfigItem(ctx, APICTokenKey) // TokenKey is a constant string representing the key for the token in the database\n\tif err != nil {\n\t\treturn APICToken{}, fmt.Errorf(\"loading token: %w\", err)\n\t}\n\n\tif token == \"\" {\n\t\treturn APICToken{}, ErrTokenNotFound\n\t}","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/token.go#L1-L37","documentation":"LoadAPICToken rejects tokens that expire within 1 minute of now (time.Now().UTC().After(exp - 1m)), returning the ErrTokenExpired sentinel so callers can distinguish 'needs refresh' from hard failures. This lets the CAPI puller fetch a fresh token rather than pull blocklists with a dying token.","triggerScenarios":"Calling LoadAPICToken when the stored token's exp claim is in the past, or within 60 seconds of the current UTC time.","commonSituations":"crowdsec offline longer than the token validity window (tokens typically last ~1h), clock skew on the host, long-lived process that never refreshed its token.","solutions":["Let the caller refresh the token from CAPI (the puller does this automatically when errors.Is(err, ErrTokenExpired))","Check host clock sync (NTP) if tokens expire prematurely","Enrollment key invalid? Re-enroll with cscli capi enroll if refresh keeps failing"],"exampleFix":"tok, err := client.LoadAPICToken(ctx)\nif errors.Is(err, dbclient.ErrTokenExpired) {\n    tok, err = refreshCAPIToken(ctx)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"tok, err := client.LoadAPICToken(ctx)\nif errors.Is(err, dbclient.ErrTokenExpired) {\n    tok, err = fetchFreshCAPIToken(ctx) // retry with new token\n}\nif err != nil {\n    return err\n}","preventionTips":["Schedule periodic token refresh before expiry (1-minute margin exists)","Keep host clocks NTP-synced","Treat this error as 'refresh now', not fatal"],"tags":["jwt","expiry","capi","crowdsec"],"backgroundTag":"jwt-token-expired","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"}