crowdsecurity/crowdsec · error · ParseTimeFail
stop_at field time '%s': %w: %w
Error message
stop_at field time '%s': %w: %w
What it means
Same failure mode as the start_at check but for alertItem.StopAt: time.Parse(time.RFC3339, *alertItem.StopAt) failed while building the alert in UpdateCommunityBlocklist, so the call aborts with the value, the parse error, and the ParseTimeFail sentinel. Note the code below it parses StopAt a second time and merely falls back to time.Now() on failure — only the first parse is fatal.
Source
Thrown at pkg/database/alerts.go:213
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()
}
alertB := c.Ent.Alert.
Create().
SetScenario(*alertItem.Scenario).
SetMessage(*alertItem.Message).
SetEventsCount(*alertItem.EventsCount).
SetStartedAt(startAtTime).
SetStoppedAt(stopAtTime).
SetSourceScope(*alertItem.Source.Scope).
SetSourceValue(*alertItem.Source.Value).View on GitHub (pinned to 909b515798)
Solutions
- Fix the source to emit RFC3339 timestamps (UTC, e.g. '2026-09-06T12:00:00Z')
- Normalize the timestamp in the caller before SaveAlerts
- Use errors.Is(err, database.ParseTimeFail) to detect and skip/re-queue the offending alert
Example fix
// before alert.StopAt = ptr(startAt.Add(4*time.Hour).String()) // after alert.StopAt = ptr(startAt.Add(4*time.Hour).UTC().Format(time.RFC3339))
Defensive patterns
Strategy: validation
Validate before calling
if alert.StopAt == nil {
return errors.New("alert missing stop_at")
}
if _, err := time.Parse(time.RFC3339, *alert.StopAt); err != nil {
return fmt.Errorf("invalid stop_at %q: %w", *alert.StopAt, 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 stop_at: %v", err)
return nil
}
return err
} Prevention
- Emit stop_at/expiry in RFC3339 UTC from any custom list producer
- Round-trip test serialized alerts (marshal/unmarshal) to catch format drift
- Check errors.Is(err, ParseTimeFail) to skip single bad alerts instead of failing the batch
When it happens
Trigger: UpdateCommunityBlocklist (via SaveAlerts) receives an alert whose StopAt string is empty or not RFC3339-compatible (missing timezone offset, space instead of 'T', epoch seconds, etc.).
Common situations: Blocklist/CAPI pull with malformed stop_at from a subscribed list; scripts that construct models.Alert with Go's default time.String() output; timezone-less timestamps produced by third-party tooling.
Related errors
- start_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/055dcfaaa8a63692.
Report an issue: GitHub.