{"record":{"id":"5ae2dfe882d95cdd","repo":"jaegertracing/jaeger","slug":"w-enable-the-q-feature-gate-to-use-it","errorCode":null,"errorMessage":"%w: enable the %q feature gate to use it","messagePattern":"%w: enable the %q feature gate to use it","errorType":"exception","errorClass":"ErrFilterDisabled","httpStatus":null,"severity":"warning","filePath":"cmd/jaeger/internal/extension/jaegerquery/querysvc/service.go","lineNumber":208,"sourceCode":"// prepareSearchQuery settles a search before it is dispatched: it refuses a request this\n// deployment does not accept, gives the configured query interceptors their say, and returns the\n// query to dispatch in the shape the backend understands, along with the context to dispatch it\n// with. One place decides, so that every caller gets the same answer instead of each backend's\n// own (ADR-013).\n//\n// The interceptors run after the caller's request is validated and before the backend's\n// capabilities are consulted. So an interceptor is never shown a request jaeger-query was going\n// to refuse anyway, and a predicate an interceptor adds is held to the same capability check as\n// one the caller sent.\nfunc (qs QueryService) prepareSearchQuery(\n\tctx context.Context,\n\tquery TraceQueryParams,\n) (context.Context, TraceQueryParams, error) {\n\tif query.Filter != nil {\n\t\t// None of these refusals depends on the backend, so they come before the capability call\n\t\t// rather than after it.\n\t\tif !StructuredFiltersGate.IsEnabled() {\n\t\t\treturn ctx, query, fmt.Errorf(\"%w: enable the %q feature gate to use it\",\n\t\t\t\tErrFilterDisabled, StructuredFiltersGate.ID())\n\t\t}\n\t\tif err := query.EnsureFilterStandsAlone(); err != nil {\n\t\t\treturn ctx, query, err\n\t\t}\n\t\t// Decoding a filter validates nothing, so it is finalized — validated and normalized —\n\t\t// here, on behalf of every API layer above (RFC 0005 §7).\n\t\tfinalized, err := expression.Finalize(query.Filter)\n\t\tif err != nil {\n\t\t\treturn ctx, query, fmt.Errorf(\"%w: %w\", tracestore.ErrFilterInvalid, err)\n\t\t}\n\t\tquery.Filter = finalized\n\t}\n\tif len(qs.options.Interceptors) > 0 {\n\t\tvar err error\n\t\tctx, query, err = qs.onQuery(ctx, query)\n\t\tif err != nil {\n\t\t\treturn ctx, query, err","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/cmd/jaeger/internal/extension/jaegerquery/querysvc/service.go#L190-L226","documentation":"ErrFilterDisabled is returned when a trace search carries a structured query filter but the StructuredFilters feature gate is not enabled on the jaeger-query service. The gate check runs before any backend capability check because the refusal is purely local. Clients can detect it via querysvc.IsBadRequest (400-class) rather than treating it as a server fault.","triggerScenarios":"prepareSearchQuery (called during FindTraces) sees query.Filter != nil while StructuredFiltersGate.IsEnabled() is false — i.e. the client submitted a structured filter to a server started without the feature gate enabled.","commonSituations":"Jaeger UI/API client using the new structured-filter search against an older or gate-disabled jaeger-query deployment; operators upgrading the UI before enabling the gate on the server; feature-flag rollout where only some query instances have the gate on.","solutions":["Enable the feature gate whose ID appears in the message on the jaeger-query extension configuration and restart.","If the gate cannot be enabled yet, have clients stop sending the filter field in search requests.","Check errors.Is(err, querysvc.ErrFilterDisabled) client-side to surface a clear 400 message instead of a generic failure.","Verify all query replicas have the gate enabled to avoid inconsistent behavior behind a load balancer."],"exampleFix":"// before (jaeger-query config)\njaeger_query:\n  # structured filters gate not enabled\n// after\njaeger_query:\n  feature_gates:\n    - <gate-id-from-error-message>","handlingStrategy":"validation","validationCode":"if query.Filter != nil && !structuredFiltersGateEnabled {\n    return fmt.Errorf(\"structured filters are disabled on this deployment: %w\", querysvc.ErrFilterDisabled)\n}","typeGuard":null,"tryCatchPattern":"resp, err := client.FindTraces(ctx, query)\nif err != nil && errors.Is(err, querysvc.ErrFilterDisabled) {\n    // handle as HTTP 400: prompt user to enable the gate or drop the filter\n    return badRequest(err)\n}","preventionTips":["Enable the structured filters gate in every environment where clients send filters.","Gate client-side filter UI on a server capabilities check before showing it.","Check deployment parity so all query replicas share gate settings."],"tags":["feature-gate","config","query-filter","bad-request"],"backgroundTag":"feature-gate-disabled","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}