crowdsecurity/crowdsec · error · ParseTimeFail
start_at field time '%s': %w: %w
Error message
start_at field time '%s': %w: %w
What it means
UpdateCommunityBlocklist requires alertItem.StartAt to be a RFC3339-formatted timestamp string; when time.Parse(time.RFC3339, *alertItem.StartAt) fails, the raw value and the parse error are wrapped with the sentinel ParseTimeFail so callers can test for it. It indicates the alert payload (normally received from CAPI) carries a malformed start_at timestamp.
Source
Thrown at pkg/database/alerts.go:204
return "", nil
}
// UpdateCommunityBlocklist is called to update either the community blocklist (or other lists the user subscribed to)
// it takes care of creating the new alert with the associated decisions, and it will as well deleted the "older" overlapping decisions:
// 1st pull, you get decisions [1,2,3]. it inserts [1,2,3]
// 2nd pull, you get decisions [1,2,3,4]. it inserts [1,2,3,4] and will try to delete [1,2,3,4] with a different alert ID and same origin
func (c *Client) UpdateCommunityBlocklist(ctx context.Context, alertItem *models.Alert) (int, int, int, error) {
if alertItem == nil {
return 0, 0, 0, errors.New("nil alert")
}
if alertItem.StartAt == nil {
return 0, 0, 0, errors.New("nil start_at")
}
startAtTime, err := time.Parse(time.RFC3339, *alertItem.StartAt)
if err != nil {
return 0, 0, 0, fmt.Errorf("start_at field time '%s': %w: %w", *alertItem.StartAt, err, ParseTimeFail)
}
if alertItem.StopAt == nil {
return 0, 0, 0, errors.New("nil stop_at")
}
stopAtTime, err := time.Parse(time.RFC3339, *alertItem.StopAt)
if err != nil {
return 0, 0, 0, fmt.Errorf("stop_at field time '%s': %w: %w", *alertItem.StopAt, err, ParseTimeFail)
}
ts, err := time.Parse(time.RFC3339, *alertItem.StopAt)
if err != nil {
c.Log.Errorf("While parsing StartAt of item %s : %s", *alertItem.StopAt, err)
ts = time.Now().UTC()
}
View on GitHub (pinned to 909b515798)
Solutions
- Log the offending StartAt value and fix the producer to emit RFC3339 (e.g. time.Now().UTC().Format(time.RFC3339))
- Pre-parse/normalize timestamps before calling SaveAlerts/UpdateCommunityBlocklist
- Check errors.Is(err, database.ParseTimeFail) in the caller to distinguish parse failures from DB errors and skip the bad alert instead of aborting the whole pull
Example fix
// before
alert.StartAt = ptr("2026-09-06 12:00:00")
// after
alert.StartAt = ptr(time.Now().UTC().Format(time.RFC3339)) Defensive patterns
Strategy: validation
Validate before calling
if alert.StartAt == nil {
return errors.New("alert missing start_at")
}
if _, err := time.Parse(time.RFC3339, *alert.StartAt); err != nil {
return fmt.Errorf("invalid start_at %q: %w", *alert.StartAt, err)
} Type guard
func validRFC3339(s *string) bool {
if s == nil { return false }
_, err := time.Parse(time.RFC3339, *s)
return err == nil
} Try / catch
if _, _, _, err := db.UpdateCommunityBlocklist(ctx, alert); err != nil {
if errors.Is(err, database.ParseTimeFail) {
log.Warnf("skipping alert with bad start_at: %v", err)
return nil
}
return err
} Prevention
- Always produce timestamps with t.UTC().Format(time.RFC3339)
- Never hand-format timestamps with spaces or local zones in alert payloads
- Test list-source payloads for RFC3339 conformance before ingest
When it happens
Trigger: Calling UpdateCommunityBlocklist (directly or via SaveAlerts when processing pulled CAPI/community-blocklist alerts) with an alert whose StartAt is empty, non-RFC3339 (e.g. '2024-01-02 15:04:05' without timezone, unix epoch, or locale format).
Common situations: A custom or third-party blocklist source pushes alerts with non-RFC3339 dates; a data-conversion/migration script builds models.Alert by hand; an older/newer CAPI payload format differs.
Related errors
- stop_at field time '%s': %w: %w
- timestamp is not valid
- timestamp is not valid
- blocklist URL is nil
- pull already in progress
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/f1cf9ed7283c1ed7.
Report an issue: GitHub.