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
- Send the constant as a string: store/compare the tag value as its string form (e.g. '200' instead of 200).
- Use an untyped *expression.AnyValue constant, which matches the attribute regardless of stored form.
- 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
- Store and compare tag values as strings in filters
- Use AnyValue for untyped tag equality
- Never build numeric/bool constants against attribute or name fields
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
- %w: it does not support the built-in field %q of the %q leve
- %w: it compares %q against a duration such as "2s" only
- %w: it does not support the operator %q
- %w: it does not support the operator %q on %q
- %w: it can carry only one predicate on %q
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/b1ea81ea3ae3735f.
Report an issue: GitHub.