jaegertracing/jaeger · error · ErrFilterUnsupported

%w: it compares %q against a string constant only

Error message

%w: it compares %q against a string constant only

What it means

The legacy scalar fields (attributes, service name, operation name) are string-valued, so an equality predicate can only carry a string constant (*expression.StringValue) or an untyped constant (*expression.AnyValue) that represents a tag match with no declared type. A typed non-string constant — a number, boolean, or duration literal — names a type these fields cannot match, so textConstant refuses it with ErrFilterUnsupported. This happens while downconverting a filter to the legacy shape for backends without native filter support.

Source

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

		}
		query.DurationMax = constant.Value
	default:
		return errUnsupportedOperatorOn(op, name)
	}
	return nil
}

// textConstant reads the constant a string-valued legacy field can carry: a text constant, or an
// untyped one, which is what an unqualified tag equality has always been. A constant of any other
// type asks for a match on a type these fields cannot name.
func textConstant(name string, value expression.Expression) (string, error) {
	switch constant := value.(type) {
	case *expression.StringValue:
		return constant.Value, nil
	case *expression.AnyValue:
		return constant.Value, nil
	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)
}

View on GitHub (pinned to 806f444784)

Solutions

  1. Send the constant as a string: store/compare the tag value as its string form (e.g. '200' instead of 200).
  2. Use an untyped *expression.AnyValue constant, which matches the attribute regardless of stored form.
  3. Drop the predicate or use a backend with native typed filter support.

Example fix

// before
Args: []expression.Expression{&expression.AttributeRef{Key: "http.status_code"}, &expression.Int64Value{Value: 200}}
// after
Args: []expression.Expression{&expression.AttributeRef{Key: "http.status_code"}, &expression.AnyValue{Value: "200"}}
Defensive patterns

Strategy: validation

Validate before calling

func isStringLikeConstant(v expression.Expression) bool {
  switch v.(type) {
  case *expression.StringValue, *expression.AnyValue:
    return true
  }
  return false
}

Type guard

func isStringLikeConstant(v expression.Expression) bool {
  switch v.(type) {
  case *expression.StringValue, *expression.AnyValue:
    return true
  default:
    return false
  }
}

Try / catch

legacy, err := params.ToLegacyShape()
if errors.Is(err, tracestore.ErrFilterUnsupported) && strings.Contains(err.Error(), "string constant only") {
  return fmt.Errorf("convert the constant to its string form before querying: %w", err)
}

Prevention

When it happens

Trigger: ToLegacyShape() encounters a flat-conjunction predicate of the form <AttributeRef|FieldRef(service|name)> == constant where the constant is neither *expression.StringValue nor *expression.AnyValue — e.g. comparing an attribute against a numeric or duration literal.

Common situations: A client builds a typed comparison such as http.status_code == 200 (int constant) or an attribute compared against a bool/duration; the backend only serves string tag equality, so the typed constant is rejected during shape conversion.

Related errors


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