{"record":{"id":"a1671b2dbf052fc2","repo":"jaegertracing/jaeger","slug":"w-it-compares-a-reference-against-a-constant-onl","errorCode":null,"errorMessage":"%w: it compares a reference against a constant only","messagePattern":"%w: it compares a reference against a constant only","errorType":"validation","errorClass":"ErrFilterUnsupported","httpStatus":null,"severity":"error","filePath":"internal/storage/v2/api/tracestore/shape.go","lineNumber":175,"sourceCode":"\tdefault:\n\t\treturn []*expression.Call{filter}, nil\n\t}\n}\n\n// applyAsLegacyField writes one predicate into the legacy field that carries it. A legacy field\n// holds a key and one value, so a predicate that reads no single value off the span — a\n// quantifier over the events, an operator applied to a call — has nowhere to go.\nfunc applyAsLegacyField(query *TraceQueryParams, predicate *expression.Call) error {\n\tif len(predicate.Args) != 2 {\n\t\treturn errUnsupportedOperator(predicate.Op)\n\t}\n\tswitch ref := predicate.Args[0].(type) {\n\tcase *expression.AttributeRef:\n\t\treturn applyAttribute(query, predicate.Op, ref, predicate.Args[1])\n\tcase *expression.FieldRef:\n\t\treturn applyField(query, predicate.Op, ref, predicate.Args[1])\n\tdefault:\n\t\treturn fmt.Errorf(\"%w: it compares a reference against a constant only\", ErrFilterUnsupported)\n\t}\n}\n\nfunc applyAttribute(query *TraceQueryParams, op expression.Operator, ref *expression.AttributeRef, value expression.Expression) error {\n\tif op != expression.OpEq {\n\t\treturn errUnsupportedOperatorOn(op, ref.Key)\n\t}\n\tif !legacyFilterLevels.SupportsLevel(ref.Level) {\n\t\treturn fmt.Errorf(\"%w: it does not index the %q level\", ErrFilterUnsupported, ref.Level)\n\t}\n\ttext, err := textConstant(ref.Key, value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, ok := query.Attributes.Get(ref.Key); ok {\n\t\treturn errRepeatedPredicateOn(ref.Key)\n\t}\n\tquery.Attributes.PutStr(ref.Key, text)","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/v2/api/tracestore/shape.go#L157-L193","documentation":"applyAsLegacyField converts a predicate Call into a legacy TraceQueryParams field. The predicate's first argument must be an AttributeRef or FieldRef; anything else (e.g. a constant on the left, a nested Call) is not a comparable reference and yields ErrFilterUnsupported with 'compares a reference against a constant only'.","triggerScenarios":"Calling ToLegacyShape with a predicate whose Args[0] is neither *expression.AttributeRef nor *expression.FieldRef — e.g. constant-first comparisons or function-call operands.","commonSituations":"Filters built with reversed operand order (constant == attribute) or with computed expressions on the left side, then converted to the legacy query shape which only supports ref-vs-constant comparisons.","solutions":["Rewrite the comparison so the reference (attribute or field) is the first operand and the constant the second.","Use a filter-based Reader path instead of the legacy shape if you need general comparisons.","Pre-validate predicate argument order before conversion."],"exampleFix":"// before\nexpression.Eq(expression.Constant(\"svc\"), expression.SpanAttribute(\"service.name\"))\n// after\nexpression.Eq(expression.SpanAttribute(\"service.name\"), expression.Constant(\"svc\"))","handlingStrategy":"validation","validationCode":"func refFirst(c *expression.Call) bool {\n  switch c.Args[0].(type) {\n  case *expression.AttributeRef, *expression.FieldRef:\n    return true\n  }\n  return false\n}\n// ensure all predicates pass before conversion","typeGuard":"func asRef(e expression.Expression) (expression.Expression, bool) {\n  switch e.(type) {\n  case *expression.AttributeRef, *expression.FieldRef:\n    return e, true\n  }\n  return nil, false\n}","tryCatchPattern":"shape, err := tracestore.ToLegacyShape(filter)\nif err != nil {\n  if errors.Is(err, tracestore.ErrFilterUnsupported) && strings.Contains(err.Error(), \"constant\") {\n    filter = normalizeOperandOrder(filter)\n  }\n}","preventionTips":["Always put the attribute/field reference as the first operand of comparisons.","Use typed constructors (Eq(ref, constant)) that enforce operand order.","Lint filter builders for constant-first comparisons."],"tags":["filter","shape","tracestore"],"backgroundTag":"unsupported-filter-operator","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}