{"record":{"id":"7edfb22135cb0d38","repo":"jaegertracing/jaeger","slug":"w-it-evaluates-a-conjunction-of-predicates-only","errorCode":null,"errorMessage":"%w: it evaluates a conjunction of predicates only","messagePattern":"%w: it evaluates a conjunction of predicates only","errorType":"validation","errorClass":"ErrFilterUnsupported","httpStatus":null,"severity":"error","filePath":"internal/storage/v2/api/tracestore/shape.go","lineNumber":148,"sourceCode":"\t\t\treturn query, err\n\t\t}\n\t}\n\treturn rewritten, nil\n}\n\n// flatConjuncts returns the leaf predicates of a conjunction. A nested `and` means the same\n// as a flat one, so it is flattened rather than refused; `or` and `not` have no legacy form,\n// so they are refused as the operators they are.\nfunc flatConjuncts(filter *expression.Call) ([]*expression.Call, error) {\n\tswitch filter.Op {\n\tcase expression.OpOr, expression.OpNot:\n\t\treturn nil, errUnsupportedOperator(filter.Op)\n\tcase expression.OpAnd:\n\t\tconjuncts := make([]*expression.Call, 0, len(filter.Args))\n\t\tfor _, arg := range filter.Args {\n\t\t\tnested, ok := arg.(*expression.Call)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"%w: it evaluates a conjunction of predicates only\", ErrFilterUnsupported)\n\t\t\t}\n\t\t\tinner, err := flatConjuncts(nested)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tconjuncts = append(conjuncts, inner...)\n\t\t}\n\t\treturn conjuncts, nil\n\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 {","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/v2/api/tracestore/shape.go#L130-L166","documentation":"flatConjuncts flattens an AND conjunction for ToLegacyShape conversion. Every argument of an OpAnd call must itself be a Call; a non-Call argument (e.g. a bare comparison constant or malformed node) cannot be expressed in the legacy conjunctive shape and yields ErrFilterUnsupported with 'conjunction of predicates only'.","triggerScenarios":"Calling ToLegacyShape on a filter where an OpAnd node has a non-Call argument — i.e. a malformed or non-predicate expression nested directly under AND.","commonSituations":"Programmatically constructed expression trees with wrong node types; filters built by external tools that don't enforce the predicate-only AND contract.","solutions":["Ensure every AND argument is a predicate Call (comparison or nested AND).","Rebuild the expression using the expression package constructors instead of hand-built nodes.","Catch this and fall back to the filter-capabilities path instead of legacy shape conversion."],"exampleFix":"// before\nexpression.Call{Op: expression.OpAnd, Args: []expression.Expression{pred, someConstant}}\n// after\nexpression.Call{Op: expression.OpAnd, Args: []expression.Expression{pred1, pred2}}","handlingStrategy":"validation","validationCode":"func isPredicate(e expression.Expression) bool {\n  _, ok := e.(*expression.Call)\n  return ok\n}\n// assert every AND arg is a Call before calling ToLegacyShape","typeGuard":"func isCallNode(e expression.Expression) (*expression.Call, bool) {\n  c, ok := e.(*expression.Call)\n  return c, ok\n}","tryCatchPattern":"shape, err := tracestore.ToLegacyShape(filter)\nif err != nil {\n  if errors.Is(err, tracestore.ErrFilterUnsupported) {\n    return queryViaFilterPath(filter) // skip legacy conversion\n  }\n  return err\n}","preventionTips":["Build expression trees only with the expression package constructors.","Never hand-assemble Call nodes with raw arguments.","Round-trip test filter builders through ToLegacyShape."],"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"}