{"record":{"id":"8fc633e373260d1b","repo":"crowdsecurity/crowdsec","slug":"saving-token-w","errorCode":null,"errorMessage":"saving token: %w","messagePattern":"saving token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/token.go","lineNumber":67,"sourceCode":"\t}\n\n\texpFloat, ok := claims[\"exp\"].(float64)\n\tif !ok {\n\t\treturn APICToken{}, fmt.Errorf(\"%w: exp\", ErrTokenMissingClaim)\n\t}\n\n\texp := time.Unix(int64(expFloat), 0)\n\tif time.Now().UTC().After(exp.Add(-1 * time.Minute)) {\n\t\treturn APICToken{}, ErrTokenExpired\n\t}\n\n\treturn APICToken{Raw: token, ExpiresAt: exp}, nil\n}\n\n// SaveAPICToken stores the given JWT token in the local database under the appropriate config item.\nfunc (c *Client) SaveAPICToken(ctx context.Context, token string) error {\n\tif err := c.SetConfigItem(ctx, APICTokenKey, token); err != nil {\n\t\treturn fmt.Errorf(\"saving token: %w\", err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":49,"sourceCodeEnd":72,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/token.go#L49-L72","documentation":"SaveAPICToken stores the CAPI JWT under APICTokenKey in the config-items table via SetConfigItem. If that write fails, this wrapped error is returned. The token will not be persisted, so subsequent runs will fall back to ErrTokenNotFound on LoadAPICToken.","triggerScenarios":"Calling SaveAPICToken (after CAPI login/register) when SetConfigItem fails: SQLite write lock held by another process, disk full, read-only filesystem, corrupted DB, canceled context.","commonSituations":"Disk quota exhausted in /var/lib/crowdsec; DB locked by a long cscli operation during automated login; read-only mount after a maintenance window.","solutions":["Check the wrapped error for the DB cause (locked / disk full / read-only).","Free disk space and ensure the data directory is writable by the crowdsec user.","Resolve concurrent DB writers or enable WAL mode.","Retry the authentication flow — without a saved token every CAPI call will re-login until the write succeeds."],"exampleFix":"// before\nif err := c.SaveAPICToken(ctx, token); err != nil { return err }\n// after: retry transient write failures\nif err := c.SaveAPICToken(ctx, token); err != nil {\n    if errors.Is(err, os.ErrPermission) || strings.Contains(err.Error(), \"readonly\") {\n        return fmt.Errorf(\"cannot persist CAPI token, check /var/lib/crowdsec: %w\", err)\n    }\n    return fmt.Errorf(\"saving CAPI token: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// pre-check storage before saving the token\nif fi, err := os.Stat(dataDir); err != nil || !fi.IsDir() { return fmt.Errorf(\"data dir missing: %s\", dataDir) }\nif f, err := os.CreateTemp(dataDir, \".wtest\"); err != nil { return err } else { f.Close(); os.Remove(f.Name()) }","typeGuard":null,"tryCatchPattern":"err := client.SaveAPICToken(ctx, token)\nif err != nil {\n    log.Warnf(\"could not persist CAPI token, re-login will be required: %v\", err)\n    return fmt.Errorf(\"token persistence: %w\", err)\n}","preventionTips":["Ensure /var/lib/crowdsec is writable and has free space for the crowdsec user.","Avoid long-held DB locks from concurrent cscli commands during login flows.","Retry token saves; without persistence every run re-authenticates.","Watch for read-only remounts after maintenance windows."],"tags":["database","sqlite","token","write-failure","capi"],"backgroundTag":"database-write-failed","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"}