jaegertracing/jaeger · error · ErrFilterInvalid

%w: it cannot be combined with %v; express those predicates

Error message

%w: it cannot be combined with %v; express those predicates in the filter instead

What it means

EnsureFilterStandsAlone validates that a TraceQueryParams does not set legacy query fields (service, operation, tags, duration/minDuration, startTime/endTime, attributes) together with a filter expression. If both are present it returns ErrFilterInvalid wrapped with the list of offending fields, because the two filtering models would over-constrain the query.

Source

Thrown at internal/storage/v2/api/tracestore/admission.go:45

	if q.ServiceName != "" {
		set = append(set, "service_name")
	}
	if q.OperationName != "" {
		set = append(set, "operation_name")
	}
	if q.DurationMin != 0 {
		set = append(set, "duration_min")
	}
	if q.DurationMax != 0 {
		set = append(set, "duration_max")
	}
	if q.Attributes != (pcommon.Map{}) && q.Attributes.Len() > 0 {
		set = append(set, "attributes")
	}
	if len(set) == 0 {
		return nil
	}
	return fmt.Errorf("%w: it cannot be combined with %v; express those predicates in the filter instead",
		ErrFilterInvalid, set)
}

// ForCapabilities gives the Reader whichever of the two filtering models it declared it can
// evaluate. A Reader that declares filter support gets the filter itself, once every level and
// operator it uses is one that Reader listed. A Reader that declares none gets the filter rewritten
// into the legacy predicate fields, which carry the equalities and inclusive duration bounds and
// nothing else (ToLegacyShape), or a refusal where they cannot carry it.
//
// It answers only that question. Whether the request is one this deployment accepts at all is the
// caller's to settle first.
func (q TraceQueryParams) ForCapabilities(caps SearchCapabilities) (TraceQueryParams, error) {
	if q.Filter == nil {
		return q, nil
	}
	if caps.Filter.IsEmpty() {
		return q.ToLegacyShape()
	}

View on GitHub (pinned to 806f444784)

Solutions

  1. Move every legacy predicate into the filter expression and clear the legacy fields.
  2. If you only need legacy matching, drop the filter entirely.
  3. Pre-validate with EnsureFilterStandsAlone before invoking the Reader.

Example fix

// before
query := tracestore.TraceQueryParams{ServiceName: "svc", Filter: expr}
// after
query := tracestore.TraceQueryParams{
  Filter: expression.And(
    expression.Eq(expression.SpanAttribute("service.name"), "svc"),
    expr,
  ),
}
Defensive patterns

Strategy: validation

Validate before calling

if err := tracestore.EnsureFilterStandsAlone(query.Filter); err != nil {
  return fmt.Errorf("invalid query: %w", err)
}
// also zero out legacy fields when a filter is set
if query.Filter != nil {
  query.ServiceName, query.Operation = "", ""
  query.Attributes = pcommon.NewMap()
}

Try / catch

if err := reader.FindTraces(ctx, traces, query); err != nil {
  if errors.Is(err, tracestore.ErrFilterInvalid) {
    return fmt.Errorf("legacy fields conflict with filter: %w", err)
  }
  return err
}

Prevention

When it happens

Trigger: Calling a v2 trace Reader's FindTraceIDs/FindTraces with a TraceQueryParams whose ServiceName/Operation/Attributes/TimeRange etc. are non-zero while a filter Call is also supplied, e.g. via prepareSearchQuery or toTraceQueryParams paths.

Common situations: Migrating code from the legacy query API to filter expressions while keeping old field assignments; UI/API layers that build both forms of predicates unconditionally.

Related errors


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