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

  1. Fix the source to emit RFC3339 timestamps (UTC, e.g. '2026-09-06T12:00:00Z')
  2. Normalize the timestamp in the caller before SaveAlerts
  3. 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

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


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/055dcfaaa8a63692. Report an issue: GitHub.