{"record":{"id":"45b8d3d1c423f201","repo":"crowdsecurity/crowdsec","slug":"failed-to-parse-last-timestamp-w","errorCode":null,"errorMessage":"failed to parse last timestamp: %w","messagePattern":"failed to parse last timestamp: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/apiserver/papi.go","lineNumber":261,"sourceCode":"\tif err != nil {\n\t\tp.Logger.Warningf(\"failed to get last timestamp for papi pull: %s\", err)\n\t}\n\n\t// value doesn't exist, it's first time we're pulling\n\tif lastTimestampStr == \"\" {\n\t\tbinTime, err := lastTimestamp.MarshalText()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to serialize last timestamp: %w\", err)\n\t\t}\n\n\t\tif err := p.DBClient.SetConfigItem(ctx, PapiPullKey, string(binTime)); err != nil {\n\t\t\tp.Logger.Errorf(\"error setting papi pull last key: %s\", err)\n\t\t} else {\n\t\t\tp.Logger.Debugf(\"config item '%s' set in database with value '%s'\", PapiPullKey, string(binTime))\n\t\t}\n\t} else {\n\t\tif err := lastTimestamp.UnmarshalText([]byte(lastTimestampStr)); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to parse last timestamp: %w\", err)\n\t\t}\n\t}\n\n\ttokenRefreshChan := p.apiClient.GetTokenRefreshChan()\n\tvar papiChan chan longpollclient.Event // Chan is nil by default to block until PAPI actually establishes the connection\n\tpapiCtx, cancel := context.WithCancel(ctx)\n\n\tcurrentSubscriptionType := p.apiClient.GetSubscriptionType()\n\n\tp.Logger.Debugf(\"current subscription type is %s\", currentSubscriptionType)\n\n\tif currentSubscriptionType == apiclient.SubscriptionTypeEnterprise || currentSubscriptionType == apiclient.SubscriptionTypeSecOps {\n\t\t// If allowed to use PAPI, start it\n\t\t// Otherwise it will be started when the token is refreshed with an ent subscription\n\t\tp.Logger.Infof(\"Starting PAPI pull (since:%s)\", lastTimestamp)\n\t\tpapiChan = p.Client.Start(papiCtx, lastTimestamp)\n\t}\n","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiserver/papi.go#L243-L279","documentation":"Pull reads the previously stored last-pull timestamp from the database and parses it with UnmarshalText. This error means the stored PapiPullKey value is not a valid RFC3339 text timestamp, so pulling cannot resume from the saved position.","triggerScenarios":"lastTimestampStr is non-empty but UnmarshalText fails — the config item in the database was manually edited, written by an incompatible version, or corrupted.","commonSituations":"Manual DB manipulation of config_items, downgrade/upgrade across versions with changed storage format, partial write from a crashed run.","solutions":["Delete the PapiPullKey row from config_items so Pull reinitializes from scratch","Inspect the stored value and correct it to a valid RFC3339 timestamp","Confirm no other tooling is writing to config_items"],"exampleFix":"// before (manually corrupted value in DB)\nUPDATE config_items SET value='yesterday' WHERE name='papi_pull_last';\n// after\nDELETE FROM config_items WHERE name='papi_pull_last';","handlingStrategy":"validation","validationCode":"if v := db.GetConfigItem(PapiPullKey); v != \"\" { if _, err := time.Parse(time.RFC3339, v); err != nil { /* delete or repair the item */ } }","typeGuard":null,"tryCatchPattern":"if err := p.Pull(ctx); err != nil { if strings.Contains(err.Error(), \"failed to parse last timestamp\") { /* reset PapiPullKey and retry pull */ } }","preventionTips":["Never hand-edit config_items rows","Back up the DB before upgrades/downgrades","Verify stored timestamps parse as RFC3339 after version changes"],"tags":["time","papi","parse"],"backgroundTag":"invalid-date-format","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}