jaegertracing/jaeger · error

failed to stop the adaptive sampling aggregator : %w

Error message

failed to stop the adaptive sampling aggregator : %w

What it means

This error wraps failures from aggregator.Close() when the adaptive sampling trace processor shuts down. The aggregator must flush pending sampling statistics and release the distributed lock; if graceful shutdown fails, the close lifecycle hook wraps the underlying error so the collector reports which component failed to stop.

Source

Thrown at cmd/jaeger/internal/processors/adaptivesampling/processor.go:62

		tp.telset.Logger,
		otelmetrics.NewFactory(tp.telset.MeterProvider),
		parts.DistLock,
		parts.SamplingStore,
	)
	if err != nil {
		return fmt.Errorf("failed to create the adaptive sampling aggregator: %w", err)
	}

	agg.Start()
	tp.aggregator = agg

	return nil
}

func (tp *traceProcessor) close(context.Context) error {
	if tp.aggregator != nil {
		if err := tp.aggregator.Close(); err != nil {
			return fmt.Errorf("failed to stop the adaptive sampling aggregator : %w", err)
		}
	}
	return nil
}

func (tp *traceProcessor) processTraces(_ context.Context, td ptrace.Traces) (ptrace.Traces, error) {
	batches := v1adapter.V1BatchesFromTraces(td)
	for _, batch := range batches {
		for _, span := range batch.Spans {
			if span.Process == nil {
				span.Process = batch.Process
			}
			tp.aggregator.HandleRootSpan(span)
		}
	}
	return td, nil
}

View on GitHub (pinned to 806f444784)

Solutions

  1. Read the wrapped inner error to identify which shutdown step failed (lock release vs worker shutdown)
  2. Ensure the distributed-lock backend is reachable during shutdown so the aggregator can release its lock
  3. Give the collector a sufficient shutdown timeout so background sampling workers can exit
  4. If it recurs, check the sampling store health; repeated Close failures usually indicate an unhealthy backend

Example fix

# before: too-short shutdown kills the aggregator mid-flush
# after: allow time for graceful stop in collector config
service:
  telemetry: ...
# run collector with a longer shutdown grace period, e.g.
# ensure dist-lock backend is up: docker compose up etcd
Defensive patterns

Strategy: try-catch

Validate before calling

if tp.aggregator != nil {
    // check backend health before shutdown to anticipate Close failures
    if err := healthCheckSamplingStore(ctx); err != nil {
        logger.Warn("sampling store unhealthy; Close may fail to release dist lock")
    }
}

Try / catch

if err := tp.close(ctx); err != nil {
    logger.Error("failed to stop adaptive sampling aggregator", log.Error(err))
    // decide whether to abort shutdown or continue; inspect wrapped cause
    return fmt.Errorf("processor shutdown failed: %w", err)
}

Prevention

When it happens

Trigger: Calling close (collector shutdown or test) while tp.aggregator is non-nil and aggregator.Close() returns an error — e.g. the distributed lock cannot be released or background workers fail to stop within the shutdown deadline.

Common situations: Collector shutdown with the sampling store unreachable; the dist-lock backend (etcd/oteld) down at shutdown time so the lock cannot be released; context/deadline expiring before the aggregator's background loop exits.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/98aa09cf8fe21a9e. Report an issue: GitHub.