thanos-io/thanos · warning

%s

Error message

%s

What it means

exemplarsServer.Send is the streaming receiver for ExemplarsResponse messages from downstream store/proxy servers. When a response carries a warning string instead of data, the server collects it via a multierror and returns nil so the stream continues. The message text is the warning itself ("%s"), propagated from a downstream server; this specific errors.New only fires for "empty exemplars data" — the family here is the streamed-warning path where GetWarning() != "".

Solutions

  1. Inspect resp.warnings returned from Exemplars(); the actual cause is the wrapped warning text from the failing downstream store.
  2. Check health/logs of the store API endpoints that produced the warning (unreachable stores, unsupported exemplar endpoints).
  3. If failures should be fatal, switch the query to PartialResponseStrategy_ABORT so errors abort instead of becoming warnings.
  4. Fix the underlying store error (often 'exemplars not supported' on older store gateways).
Defensive patterns

Strategy: try-catch

Try / catch

data, warnings, err := client.Exemplars(ctx, req)
if err != nil {
	// fatal path (ABORT strategy)
	return err
}
for _, w := range warnings {
	log.Warnf("exemplars warning: %v", w)
}
// still use partial `data`

Prevention

When it happens

Trigger: A downstream Exemplars server sends exemplarspb.NewWarningExemplarsResponse (e.g. from a partial-response failure in the proxy's receive loop); srv.Send observes res.GetWarning() != "" and accumulates it instead of failing.

Common situations: Query frontends with PartialResponseStrategy_WARMUP/`true` partial responses where some storeAPIs fail; the client sees warnings aggregated alongside partial exemplar data.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/74a02ef83f0948b2. Report an issue: GitHub.

Appendix: source

Thrown at pkg/exemplars/exemplars.go:49

	replicaLabels map[string]struct{}
}

type exemplarsServer struct {
	// This field just exist to pseudo-implement the unused methods of the interface.
	exemplarspb.Exemplars_ExemplarsServer
	ctx context.Context

	warnings annotations.Annotations
	data     []*exemplarspb.ExemplarData
	mu       sync.Mutex
}

func (srv *exemplarsServer) Send(res *exemplarspb.ExemplarsResponse) error {
	if res.GetWarning() != "" {
		srv.mu.Lock()
		defer srv.mu.Unlock()
		srv.warnings.Add(errors.New(res.GetWarning()))
		return nil
	}

	if res.GetData() == nil {
		return errors.New("empty exemplars data")
	}

	srv.mu.Lock()
	defer srv.mu.Unlock()
	srv.data = append(srv.data, res.GetData())
	return nil
}

func (srv *exemplarsServer) Context() context.Context {
	return srv.ctx
}

func NewGRPCClient(es exemplarspb.ExemplarsServer) *GRPCClient {

View on GitHub (pinned to 35b8b99117)