{"record":{"id":"df26f19448738f23","repo":"jaegertracing/jaeger","slug":"this-storage-backend-cannot-serve-this-query-filte","errorCode":null,"errorMessage":"this storage backend cannot serve this query filter","messagePattern":"this storage backend cannot serve this query filter","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/v2/api/tracestore/capabilities.go","lineNumber":21,"sourceCode":"\npackage tracestore\n\nimport (\n\t\"errors\"\n\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.","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/v2/api/tracestore/capabilities.go#L3-L39","documentation":"ErrFilterUnsupported signals that a tracestore Reader cannot evaluate a query filter because the filter requires capabilities (a stored built-in field of a level, or an operator on certain references) the backend did not declare via FilterCapabilities. It is returned instead of approximating the answer, so callers never silently receive a narrower result presented as the whole answer (RFC 0005 §7).","triggerScenarios":"Calling Reader search/query APIs with a filter condition referencing a field the backend serves but does not store, or using an operator (e.g. negation, regex, range) the backend supports on some references but not the one queried; the query service also returns it for limits declared through FilterCapabilities.","commonSituations":"Switching storage backends (e.g. from Elasticsearch to ClickHouse) while keeping hardcoded filters; using filter operators not supported by a flat-index backend; code written against one Reader being pointed at a less capable Reader.","solutions":["Inspect the Reader's FilterCapabilities and remove or replace the unsupported field/operator in the query filter","Use an operator that the backend supports for that reference type","Check the filter AST against RFC 0005 §7 semantics — the backend refuses rather than approximates, so narrow the query to what is declared supported","Switch to a storage backend that supports the filter, or store the field the filter references"],"exampleFix":"// before\nquery := tracestore.OperationQuery{Filter: filter.Eq(field.SpanName, value)} // backend doesn't store span_name\n// after\ncaps := reader.FilterCapabilities()\nif !caps.Supports(field.SpanName) { /* use a supported field or error out in the caller */ }","handlingStrategy":"validation","validationCode":"caps := reader.FilterCapabilities()\nif !caps.SupportsFilter(field, op) {\n    return fmt.Errorf(\"query skipped: backend does not support filter on %s\", field)\n}","typeGuard":"func filterSupported(r tracestore.Reader, f filter.Condition) bool {\n    caps := r.FilterCapabilities()\n    return caps.Supports(f.Field) && caps.SupportsOperator(f.Field, f.Op)\n}","tryCatchPattern":"deps, err := reader.Search(ctx, q)\nif errors.Is(err, tracestore.ErrFilterUnsupported) {\n    // fall back to a supported query or surface a clear 4xx to the user\n}","preventionTips":["Check FilterCapabilities before building filters","Avoid hardcoding filter fields/operators that vary by backend","Add tests that run your query set against each backend's declared capabilities"],"tags":["storage","query-filter","jaeger"],"backgroundTag":"unsupported-query-filter","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}