{"record":{"id":"46815b5975585904","repo":"crowdsecurity/crowdsec","slug":"token-missing-required-claim","errorCode":null,"errorMessage":"token missing required claim","messagePattern":"token missing required claim","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/token.go","lineNumber":18,"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","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/token.go#L1-L36","documentation":"After parsing the CAPI JWT, LoadAPICToken requires the standard 'exp' claim as a numeric (float64) value. ErrTokenMissingClaim is returned (wrapped with the claim name, e.g. 'exp') when the claim is absent or not a number, meaning expiry cannot be evaluated.","triggerScenarios":"The stored token's claims map lacks 'exp', or exp is a string/other JSON type instead of a number (token.go:53).","commonSituations":"Custom/hand-crafted JWT stored in the DB, upstream token format change, corrupted token where only some claims survive.","solutions":["Re-enroll with CAPI to obtain a fresh standards-compliant JWT","Verify the token contains a numeric exp claim (decode with jwt.io or jwt-ish tooling)","Delete the bad token row and let the puller fetch a new one"],"exampleFix":"if errors.Is(err, dbclient.ErrTokenMissingClaim) {\n    // token lacks exp: force refresh via re-enrollment\n    _ = client.DeleteAPICToken(ctx)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if errors.Is(err, dbclient.ErrTokenMissingClaim) {\n    // unusable token: clear and force re-fetch/enrollment\n    _ = client.DeleteAPICToken(ctx)\n    return refreshCAPIToken(ctx)\n}","preventionTips":["Only store JWTs issued by CAPI","Validate tokens offline (decode claims) before persisting","Keep crowdsec updated for upstream token format changes"],"tags":["jwt","claims","capi","crowdsec"],"backgroundTag":"jwt-missing-claim","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"}