grafana/k6 · error

aggregation wait period is not allowed to be zero

Error message

aggregation wait period is not allowed to be zero

What it means

Returned by newCollector (output/cloud/expv2/collect.go:72-77) when the aggregation wait period is zero. The wait period is how long a closed time bucket is kept open for late-arriving samples before it is flushed (collect.go:164 expires buckets older than now-waitPeriod); zero wait would drop every bucket's stragglers and the expiring logic relies on a non-zero window, hence the guard (with the in-code TODO about simplifying the expiring logic).

Source

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

	// 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,
		waitPeriod:        waitPeriod,
	}, nil
}

// CollectSamples drain the buffer and collect all the samples.
func (c *collector) CollectSamples(containers []metrics.SampleContainer) {
	// Distribute all newly buffered samples into related buckets
	for _, sampleContainer := range containers {
		samples := sampleContainer.GetSamples()

View on GitHub (pinned to 93accf6570)

Solutions

  1. Set the wait period to a positive whole-second value, e.g. 5s (default) or 1s for lower latency at the cost of more dropped late samples

Example fix

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

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

Strategy: validation

Validate before calling

# shell: guard the wait period
W="${K6_CLOUD_AGGREGATION_WAIT_PERIOD:-5s}"
case "$W" in 0s) echo "wait period must be >= 1s"; exit 1;; esac
export K6_CLOUD_AGGREGATION_WAIT_PERIOD="$W"

Prevention

When it happens

Trigger: Setting K6_CLOUD_AGGREGATION_WAIT_PERIOD=0s (env var feeding expv2 Config.AggregationWaitPeriod, default 5s), or programmatic construction with AggregationWaitPeriod: types.NullDurationFrom(0). Fails at Output.Start as 'failed to initialize the samples collector'.

Common situations: Trying to minimize end-to-end latency of Cloud metric delivery by removing the wait window; tuning both aggregation knobs together and setting one to zero.

Related errors


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