jaegertracing/jaeger · error
%w: it does not support the operator %q on %q
Error message
%w: it does not support the operator %q on %q
What it means
The legacy fields a filter is downconverted into accept exactly one operator each: attributes, service name and operation name accept only equality (OpEq), and span.duration accepts only inclusive bounds OpGte/OpLte. Any other operator on that field — e.g. != on a tag, > on a name, < on a duration — has no legacy representation and is refused with ErrFilterUnsupported naming the operator and field. The narrower errUnsupportedOperator variant is used when no field name is known.
Source
Thrown at internal/storage/v2/api/tracestore/shape.go:291
// errNotADuration refuses a bound that is not a length of time. An untyped constant reaching here
// was never read as a duration, which expression.ResolveConstants does on the way in, so the
// refusal says that rather than blaming the spelling the caller wrote.
func errNotADuration(name string, value expression.Expression) error {
if constant, ok := value.(*expression.AnyValue); ok {
return fmt.Errorf("%w: the bound %q on %q was never read as a duration",
ErrFilterInvalid, constant.Value, name)
}
return fmt.Errorf(`%w: it compares %q against a duration such as "2s" only`,
ErrFilterUnsupported, name)
}
func errUnsupportedOperator(op expression.Operator) error {
return fmt.Errorf("%w: it does not support the operator %q", ErrFilterUnsupported, op)
}
func errUnsupportedOperatorOn(op expression.Operator, name string) error {
return fmt.Errorf("%w: it does not support the operator %q on %q", ErrFilterUnsupported, op, name)
}
func errRepeatedPredicateOn(name string) error {
return fmt.Errorf("%w: it can carry only one predicate on %q", ErrFilterUnsupported, name)
}
View on GitHub (pinned to 806f444784)
Solutions
- Rewrite as equality where possible (e.g. negate client-side, or use the exact matching value).
- Use inclusive bounds for duration: duration >= X / duration <= Y instead of strict > / <.
- Target a backend that declares native filter/operator support so no downconversion happens.
Example fix
// before
&expression.Call{Op: expression.OpGt, Args: []expression.Expression{durField, &expression.DurationValue{Value: time.Second}}}
// after
&expression.Call{Op: expression.OpGte, Args: []expression.Expression{durField, &expression.DurationValue{Value: time.Second}}} Defensive patterns
Strategy: validation
Validate before calling
func allowedOp(op expression.Operator, ref expression.Expression) bool {
switch ref.(type) {
case *expression.AttributeRef:
return op == expression.OpEq
case *expression.FieldRef:
fr := ref.(*expression.FieldRef)
if fr.Name == expression.SpanFieldDuration {
return op == expression.OpGte || op == expression.OpLte
}
return op == expression.OpEq
}
return false
} Try / catch
legacy, err := params.ToLegacyShape()
if errors.Is(err, tracestore.ErrFilterUnsupported) {
return nil, fmt.Errorf("legacy fields support only =, and >=/<= for duration: %w", err)
} Prevention
- Use equality for attributes, service and operation names
- Use inclusive >= / <= for duration bounds
- Offer exclusive-bound and negation features only where the backend declares support
When it happens
Trigger: ToLegacyShape() sees an AttributeRef predicate with op != OpEq; a service/name FieldRef predicate with op != OpEq; or a duration FieldRef predicate with an op other than OpGte/OpLte.
Common situations: Users write attribute != value or attribute > value searches in a UI; exclusive bounds duration > 1s are requested from a legacy backend; a filter builder emits regex-like operators unsupported by the legacy shape.
Related errors
- %w: it does not support the operator %q
- %w: it does not support the built-in field %q of the %q leve
- %w: it compares %q against a string constant only
- %w: it compares %q against a duration such as "2s" only
- %w: it can carry only one predicate on %q
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/311173b4364632b6.
Report an issue: GitHub.