jaegertracing/jaeger · error · ErrFilterUnsupported
%w: it does not index the %q level
Error message
%w: it does not index the %q level
What it means
applyAttribute converts an equality predicate on an AttributeRef into the legacy query's attribute map, but only if the legacyFilterLevels capability set indexes the attribute's level. A reference to a non-indexed level (e.g. trace-level attribute where only span/span-attribute are indexed) yields ErrFilterUnsupported with the level name.
Source
Thrown at internal/storage/v2/api/tracestore/shape.go:184
if len(predicate.Args) != 2 {
return errUnsupportedOperator(predicate.Op)
}
switch ref := predicate.Args[0].(type) {
case *expression.AttributeRef:
return applyAttribute(query, predicate.Op, ref, predicate.Args[1])
case *expression.FieldRef:
return applyField(query, predicate.Op, ref, predicate.Args[1])
default:
return fmt.Errorf("%w: it compares a reference against a constant only", ErrFilterUnsupported)
}
}
func applyAttribute(query *TraceQueryParams, op expression.Operator, ref *expression.AttributeRef, value expression.Expression) error {
if op != expression.OpEq {
return errUnsupportedOperatorOn(op, ref.Key)
}
if !legacyFilterLevels.SupportsLevel(ref.Level) {
return fmt.Errorf("%w: it does not index the %q level", ErrFilterUnsupported, ref.Level)
}
text, err := textConstant(ref.Key, value)
if err != nil {
return err
}
if _, ok := query.Attributes.Get(ref.Key); ok {
return errRepeatedPredicateOn(ref.Key)
}
query.Attributes.PutStr(ref.Key, text)
return nil
}
// applyField writes a predicate on a built-in field into the legacy field that holds it. Only
// three of the built-ins have one, and a predicate on any of the others is refused.
func applyField(query *TraceQueryParams, op expression.Operator, ref *expression.FieldRef, value expression.Expression) error {
switch {
case isField(ref, expression.LevelResource, expression.ResourceFieldService):
return applyText(&query.ServiceName, op, ref.Name, value)View on GitHub (pinned to 806f444784)
Solutions
- Reference attributes at a level the legacy shape supports (span attributes).
- Keep the filter-based query path for levels the legacy shape cannot express.
- Pre-check ref.Level against legacyFilterLevels.SupportsLevel before converting.
Example fix
// before
expression.Eq(expression.TraceAttribute("env"), expression.Constant("prod"))
// after
expression.Eq(expression.SpanAttribute("env"), expression.Constant("prod")) Defensive patterns
Strategy: validation
Validate before calling
if !legacyFilterLevels.SupportsLevel(ref.Level) {
return fmt.Errorf("level %q not supported by legacy shape", ref.Level)
} Type guard
func legacyIndexable(ref *expression.AttributeRef) bool {
return legacyFilterLevels.SupportsLevel(ref.Level)
} Try / catch
shape, err := tracestore.ToLegacyShape(filter)
if err != nil {
if errors.Is(err, tracestore.ErrFilterUnsupported) && strings.Contains(err.Error(), "level") {
return queryViaFilterPath(filter) // legacy shape cannot express it
}
return err
} Prevention
- Prefer span-attribute level for legacy-compatible filters.
- Check SupportsLevel before emitting each predicate in builders.
- Fall back to the filter query path when levels exceed legacy support.
When it happens
Trigger: Calling ToLegacyShape with an Eq predicate whose AttributeRef has a Level outside legacyFilterLevels, and the operator is Eq (non-Eq fails earlier via errUnsupportedOperatorOn).
Common situations: Filters referencing trace-level or field-level attributes being downgraded to legacy TraceQueryParams; backend migrations where legacy stores never indexed the referenced level.
Related errors
- %w: it evaluates a conjunction of predicates only
- %w: it compares a reference against a constant only
- %w: it does not support the operator %q
- %w: it does not index the %q level
- filter argument is empty: a call argument must carry a call
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/887979ad91adfb20.
Report an issue: GitHub.