grafana/k6 · warning
context is done before all '%s' events were processed
Error message
context is done before all '%s' events were processed
What it means
The internal event system (internal/event/system.go) fans each emitted event out to all subscribers and returns a wait function that blocks until every subscriber has signaled done (doneCount == totalSubs). If the context passed to the wait function is cancelled first, it fails with "context is done before all '<type>' events were processed" — typically because the run was aborted (signal, shutdown) while a slow or stuck subscriber was still processing.
Source
Thrown at internal/event/system.go:117
}
s.logger.WithFields(logrus.Fields{
"subscribers": totalSubs,
"event": event.Type,
}).Trace("Emitted event")
return func(ctx context.Context) error {
var doneCount int
for {
if doneCount == totalSubs {
close(doneCh)
return nil
}
select {
case <-doneCh:
doneCount++
case <-ctx.Done():
return fmt.Errorf("context is done before all '%s' events were processed", event.Type)
}
}
}
}
// Unsubscribe closes the Event channel and removes the subscription with ID
// subID.
func (s *System) Unsubscribe(subID uint64) {
s.subMx.Lock()
defer s.subMx.Unlock()
var seen bool
for _, sub := range s.subscribers {
if evtCh, ok := sub[subID]; ok {
if !seen {
close(evtCh)
}
delete(sub, subID)
seen = trueView on GitHub (pinned to 93accf6570)
Solutions
- Make every subscriber non-blocking: drain channels promptly and always signal done, including on error paths
- Give the drain step its own timeout context independent of the run context so aborts don't cut off event processing mid-wait
- For embedders, treat this error as 'canceled', log it, and continue shutdown rather than failing hard
- If observed with stock k6 cloud runs, report the reproduction at https://github.com/grafana/k6/issues
Example fix
// before (embedding k6, same context aborts mid-wait)
wait := events.Emit(ctx, evt)
return wait(ctx)
// after (independent bounded drain context)
wait := events.Emit(ctx, evt)
drainCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := wait(drainCtx); err != nil {
log.Printf("event drain cancelled: %v", err) // non-fatal
} Defensive patterns
Strategy: try-catch
Validate before calling
// For embedders: bound the drain wait with a context independent of the run context
drainCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := wait(drainCtx); err != nil {
log.Printf("event drain cancelled: %v", err) // treat as cancellation, not fatal
} Try / catch
Go: after wait(ctx), check errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded); treat as a graceful-cancel path — log it and continue shutdown instead of failing the run. Reserve hard failure for subscriber panics.
Prevention
- Never block inside event subscribers; drain channels promptly and always signal done
- Give the event-drain step its own timeout context separate from the run context
- Ensure every subscriber returns on error paths so the done count can reach the total
- On aborts, allow a short grace period before hard-cancelling the context
When it happens
Trigger: Emitting an event (e.g. the execution/wakeup events used by the cloud output) and waiting on the run context, which gets cancelled by an abort; a subscriber that blocks in its handler or never drains its channel so doneCount never reaches totalSubs before ctx.Done() fires.
Common situations: Cloud-output runs aborted mid-flight while the event pipeline still has buffered work; embedding k6 as a library and subscribing to the event System without promptly consuming the event channel.
Related errors
- iteration ended before page.on handler completed executing
- influxdb's ConcurrentWrites must be a positive number
- wait for test run ready: %w
- unknown browser event: %q, must be %q
- browser.on promise rejected: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/fc054f85a806f55f.
Report an issue: GitHub.