grafana/k6 · error

aggregation period is not allowed to be zero

Error message

aggregation period is not allowed to be zero

What it means

Returned by newCollector (output/cloud/expv2/collect.go:66-68), the constructor of the cloud output's aggregation collector. The aggregation period is the size of the time buckets used to batch metric samples before pushing them to the Cloud v2 ingestion endpoint; a zero period would make bucketID division by zero (collect.go:156 divides UnixNano by the period), so it is rejected. Config reaches it from expv2 Config.AggregationPeriod, settable via K6_CLOUD_AGGREGATION_PERIOD (default 10s in the internal cloud output).

Source

Thrown at output/cloud/expv2/collect.go:67

type collector struct {
	bq      bucketQ
	nowFunc func() time.Time

	aggregationPeriod time.Duration
	waitPeriod        time.Duration

	// we should no longer have to handle metrics that have times long in the past. So instead of a
	// map, we can probably use a simple slice (or even an array!) as a ring buffer to store the
	// aggregation buckets. This should save us a some time, since it would make the lookups and WaitPeriod
	// checks basically O(1). And even if for some reason there are occasional metrics with past times that
	// don't fit in the chosen ring buffer size, we could just send them along to the buffer unaggregated
	timeBuckets map[int64]map[metrics.TimeSeries]metricValue
}

func newCollector(aggrPeriod, waitPeriod time.Duration) (*collector, error) {
	if aggrPeriod == 0 {
		return nil, errors.New("aggregation period is not allowed to be zero")
	}
	if aggrPeriod != aggrPeriod.Truncate(time.Second) {
		return nil, errors.New("aggregation period is not allowed to have sub-second precision")
	}
	if waitPeriod == 0 {
		// TODO: we could simplify the expiring logic
		// just having an internal static logic.
		// Like skip only not closed buckets bucketEnd > now.
		return nil, errors.New("aggregation wait period is not allowed to be zero")
	}
	if waitPeriod != waitPeriod.Truncate(time.Second) {
		return nil, errors.New("aggregation wait period is not allowed to have sub-second precision")
	}
	return &collector{
		bq:                bucketQ{},
		nowFunc:           time.Now,
		timeBuckets:       make(map[int64]map[metrics.TimeSeries]metricValue),
		aggregationPeriod: aggrPeriod,

View on GitHub (pinned to 93accf6570)

Solutions

  1. Set K6_CLOUD_AGGREGATION_PERIOD to a positive whole-second value, e.g. 10s (default) or 1s for finer granularity
  2. If the goal was per-sample data, understand the cloud output always aggregates in whole-second buckets; use the JSON output locally instead

Example fix

# before
K6_CLOUD_AGGREGATION_PERIOD=0s k6 run -o cloud script.js

# after
K6_CLOUD_AGGREGATION_PERIOD=1s k6 run -o cloud script.js
Defensive patterns

Strategy: validation

Validate before calling

# shell: guard the env var before invoking k6
P="${K6_CLOUD_AGGREGATION_PERIOD:-10s}"
[ "$(( $(date -d "${P%s}" +%s 2>/dev/null || echo 0) ))" -ge 1 ] 2>/dev/null || { echo "aggregation period must be >= 1s"; exit 1; }
K6_CLOUD_AGGREGATION_PERIOD="$P" k6 run -o cloud script.js

Prevention

When it happens

Trigger: Running k6 cloud / --out cloud with K6_CLOUD_AGGREGATION_PERIOD=0s, or programmatically constructing the expv2 output with Config{AggregationPeriod: types.NullDurationFrom(0)}. Output.Start calls newCollector(config.AggregationPeriod.TimeDuration(), ...) at output.go:118 and wraps the failure as 'failed to initialize the samples collector'.

Common situations: Attempting to disable aggregation to get per-sample granularity in Cloud results; misconfigured CI env vars; overriding defaults in k6-operator deployments without validating values.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/0fcccd5b16e1ee63. Report an issue: GitHub.