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

  1. Reference attributes at a level the legacy shape supports (span attributes).
  2. Keep the filter-based query path for levels the legacy shape cannot express.
  3. 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

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


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