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
- Move every legacy predicate into the filter expression and clear the legacy fields.
- If you only need legacy matching, drop the filter entirely.
- 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
- Build queries through a single helper that emits either legacy fields or a filter, never both.
- When migrating to filters, delete legacy field assignments in the same change.
- Call EnsureFilterStandsAlone in tests for query builders.
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
- %w: %w
- %w: it does not support the operator %q
- %w: it does not index the %q level
- trace_id is required
- start_time must be before end_time
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/cb7600261a968d83.
Report an issue: GitHub.