jaegertracing/jaeger · error

%w: it does not support the operator %q

Error message

%w: it does not support the operator %q

What it means

Downconverting a filter to the legacy scalar shape can only express a flat conjunction of equality/comparison predicates. The logical operators `or` and `not` have no legacy form, and a conjunction argument that is not a predicate call (wrong arity) cannot be interpreted, so flatConjuncts / applyAsLegacyField refuse the operator with ErrFilterUnsupported and the operator's name. This is a static expressiveness limit, not a transient fault.

Source

Thrown at internal/storage/v2/api/tracestore/shape.go:287

	default:
		return "", fmt.Errorf("%w: it compares %q against a string constant only", ErrFilterUnsupported, name)
	}
}

// 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

  1. Split the query into multiple requests and union the results client-side instead of using OR/NOT.
  2. Restrict the filter builder to conjunctions of two-argument predicates when targeting legacy-only backends.
  3. Use a Reader/backend with native filter support so the filter is not downconverted.

Example fix

// before
&expression.Call{Op: expression.OpOr, Args: []expression.Expression{predA, predB}}
// after — issue two queries and merge
q1 := params; q1.Filter = predA
q2 := params; q2.Filter = predB
// merge results of FindTraces(q1) and FindTraces(q2)
Defensive patterns

Strategy: validation

Validate before calling

func isLegacyConjunction(f *expression.Call) bool {
  switch f.Op {
  case expression.OpOr, expression.OpNot:
    return false
  case expression.OpAnd:
    for _, a := range f.Args { if _, ok := a.(*expression.Call); !ok { return false } }
    return true
  default:
    return len(f.Args) == 2
  }
}

Type guard

func isSupportedRootOp(f *expression.Call) bool { return f.Op != expression.OpOr && f.Op != expression.OpNot }

Try / catch

legacy, err := params.ToLegacyShape()
if errors.Is(err, tracestore.ErrFilterUnsupported) {
  return nil, fmt.Errorf("backend supports only flat AND conjunctions; rewrite or split the query: %w", err)
}

Prevention

When it happens

Trigger: ToLegacyShape() is given a Filter whose root call is OpOr or OpNot; or a conjunction contains a non-*expression.Call argument; or a predicate has an argument count other than 2 (arity mismatch surfaced against its Op).

Common situations: A UI builds (a OR b) searches against a backend that only supports legacy conjunctive filters; a hand-rolled Call with a single argument reaches the converter; a generic filter DSL feature outpaces backend legacy capabilities.

Related errors


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