{"record":{"id":"f9e779586ff8000a","repo":"jaegertracing/jaeger","slug":"invalid-query-filter","errorCode":null,"errorMessage":"invalid query filter","messagePattern":"invalid query filter","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/v2/api/tracestore/capabilities.go","lineNumber":26,"sourceCode":"\t\"slices\"\n\n\texpression \"github.com/jaegertracing/jaeger-idl/query/expression/v1\"\n)\n\n// ErrFilterUnsupported is returned for a well-formed query filter that the storage cannot\n// serve — a level it does not index, an operator it has not implemented, or a boolean\n// structure a flat index cannot evaluate (RFC 0005 §7). The query is refused rather than\n// approximated, so a caller never reads a narrower answer as the whole one. The query\n// service returns it for the limits a Reader declared through FilterCapabilities, and a\n// Reader returns it for the ones that declaration is too coarse to express — a built-in\n// field of a level it serves but does not store, or an operator it serves on some\n// references and not others.\nvar ErrFilterUnsupported = errors.New(\"this storage backend cannot serve this query filter\")\n\n// ErrFilterInvalid is returned for a query filter whose value does not fit the field it\n// compares — the kind of mistake a structural check cannot catch, because the filter AST\n// deliberately does not carry types (RFC 0005 §6.1).\nvar ErrFilterInvalid = errors.New(\"invalid query filter\")\n\n// SearchCapabilities describes how a Reader's search methods behave where backends\n// differ: which TraceQueryParams fields may be omitted, which are honored exactly\n// rather than approximated, and which combinations a backend cannot serve. Its zero\n// value is the least capable reader, so a field added here leaves every existing\n// implementation declaring the new capability unsupported.\n//\n// Fields to expect over time, each of which is a real divergence today:\n//\n//   - Whether SearchDepth is an exact limit or a hint. jaeger.api_v3's\n//     TraceQueryParameters warns of search_depth that \"some implementations might not\n//     support precise limits\", so a caller cannot tell whether a short result set means\n//     that there are no more matches or that the backend stopped early.\n//   - Which duration-query combinations hold. Cassandra reads DurationMin/DurationMax\n//     from a separate duration_index table, and it cannot combine that table with the\n//     tag index in one query, so it rejects a search that uses both\n//     (docs/adr/001-cassandra-find-traces-duration.md). The API layer stopped rejecting\n//     the combination in https://github.com/jaegertracing/jaeger/issues/1047, which did","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/v2/api/tracestore/capabilities.go#L8-L44","documentation":"ErrFilterInvalid indicates a query filter whose comparison value does not fit the field it compares — for example a non-numeric string compared against a duration field. The filter AST deliberately does not carry types (RFC 0005 §6.1), so this semantic mistake cannot be caught by structural validation and is rejected at query-preparation time.","triggerScenarios":"Calling search/query with a filter whose value has the wrong type or format for the compared field; prepareSearchQuery and EnsureFilterStandsAlone return it when the filter's value fails the field's expected shape.","commonSituations":"Building filters programmatically with untyped values from JSON/user input; typos in date/duration literals; passing ints where strings (or vice versa) are expected for a field.","solutions":["Fix the filter value so it matches the field's expected type/format","Run the filter through validation (EnsureFilterStandsAlone / structural checks) before issuing the query","Validate user-supplied filter values at the API boundary before constructing filter AST nodes"],"exampleFix":"// before\nfilter.Eq(field.Duration, \"fast\") // not a valid duration\n// after\nfilter.Gt(field.Duration, mustParseDuration(\"1s\"))","handlingStrategy":"validation","validationCode":"if err := tracestore.EnsureFilterStandsAlone(f); err != nil {\n    return fmt.Errorf(\"rejecting query: %w\", err)\n}","typeGuard":"func validDurationValue(v any) bool {\n    switch v.(type) {\n    case time.Duration, string:\n        return true\n    }\n    return false\n}","tryCatchPattern":"res, err := reader.Search(ctx, q)\nif errors.Is(err, tracestore.ErrFilterInvalid) {\n    return http.StatusBadRequest // invalid filter value, fix the caller's input\n}","preventionTips":["Validate user-supplied filter values at the API boundary","Parse durations/numbers explicitly before putting values into filter AST nodes","Test queries through prepareSearchQuery/EnsureFilterStandsAlone in CI"],"tags":["storage","query-filter","validation","jaeger"],"backgroundTag":"invalid-query-filter","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}