crowdsecurity/crowdsec · error

element at index %d is not a time.Time

Error message

element at index %d is not a time.Time

What it means

AverageInterval computes the mean duration between consecutive timestamps in a slice. When the expression runtime passes a []interface{}, each element is asserted to time.Time; if any element is a different type (string, float, nil), the function stops and returns this error with the offending index, because it cannot do time arithmetic on non-time values.

Source

Thrown at pkg/exprhelpers/helpers.go:677

// func AverageInterval(times []time.Time) time.Duration
func AverageInterval(params ...any) (any, error) {
	if len(params) != 1 {
		return 0, errors.New("AverageInterval expects exactly one parameter: a slice of times")
	}

	var times []time.Time

	// Handle both []time.Time and []interface{} (from expr map function)
	switch v := params[0].(type) {
	case []time.Time:
		times = v
	case []interface{}:
		times = make([]time.Time, len(v))
		for i, item := range v {
			t, ok := item.(time.Time)
			if !ok {
				return 0, fmt.Errorf("element at index %d is not a time.Time", i)
			}
			times[i] = t
		}
	default:
		return 0, errors.New("AverageInterval expects a slice of times")
	}

	if len(times) < 2 {
		return 0, errors.New("need at least two times to calculate an average interval")
	}

	// Sort times in ascending order
	sort.Slice(times, func(i, j int) bool {
		return times[i].Before(times[j])
	})

	var total time.Duration
	for i := 1; i < len(times); i++ {

View on GitHub (pinned to 909b515798)

Solutions

  1. Parse each element to time.Time before averaging: ensure the field is populated via a ParseDate node so elements are time.Time
  2. Check the element at the reported index in your data — it will show the unexpected type
  3. If elements are strings, convert them first (e.g. a helper that parses each entry) rather than passing raw strings
  4. Guard the expression so AverageInterval is only evaluated when the field was produced by a date parser

Example fix

// before
AverageInterval(event.GetField('leaks')) // elements are strings
// after
// ensure a parse node precedes it, e.g. list populated via ParseDate so elements are time.Time
Defensive patterns

Strategy: type-guard

Validate before calling

// expr: all elements must be times
// len(times) > 0 && ...

Type guard

func allTimes(v []interface{}) bool {
	for _, item := range v {
		if _, ok := item.(time.Time); !ok {
			return false
		}
	}
	return len(v) > 0
}

Prevention

When it happens

Trigger: Calling AverageInterval() in an expr expression on an event field that is a []interface{} containing non-time.Time elements, e.g. event.GetField('leaks') built from parsed logs where entries were stored as strings or numbers instead of time.Time.

Common situations: Parser nodes storing raw string dates into a list field instead of parsed timestamps; expression built before a date parser node (ParseDate) was applied; nil entries in the slice from missing optional fields.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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