jaegertracing/jaeger · error · tracestore.ErrFilterInvalid
%w: %w
Error message
%w: %w
What it means
prepareSearchQuery finalizes a structured search filter (expression.Finalize) before dispatching it to the storage backend. Because decoding a filter validates nothing, validation and normalization happen here on behalf of every API layer; if the filter expression is structurally or semantically invalid, the error is wrapped in tracestore.ErrFilterInvalid and returned to the caller.
Source
Thrown at cmd/jaeger/internal/extension/jaegerquery/querysvc/service.go:218
func (qs QueryService) prepareSearchQuery(
ctx context.Context,
query TraceQueryParams,
) (context.Context, TraceQueryParams, error) {
if query.Filter != nil {
// None of these refusals depends on the backend, so they come before the capability call
// rather than after it.
if !StructuredFiltersGate.IsEnabled() {
return ctx, query, fmt.Errorf("%w: enable the %q feature gate to use it",
ErrFilterDisabled, StructuredFiltersGate.ID())
}
if err := query.EnsureFilterStandsAlone(); err != nil {
return ctx, query, err
}
// Decoding a filter validates nothing, so it is finalized — validated and normalized —
// here, on behalf of every API layer above (RFC 0005 §7).
finalized, err := expression.Finalize(query.Filter)
if err != nil {
return ctx, query, fmt.Errorf("%w: %w", tracestore.ErrFilterInvalid, err)
}
query.Filter = finalized
}
if len(qs.options.Interceptors) > 0 {
var err error
ctx, query, err = qs.onQuery(ctx, query)
if err != nil {
return ctx, query, err
}
}
if query.Filter == nil {
return ctx, query, qs.checkServiceName(ctx, query)
}
caps, err := qs.traceReader.SearchCapabilities(ctx)
if err != nil {
// A reader that cannot report its capabilities reads as the least capable one, which
// serves only the legacy predicate fields.
caps = tracestore.SearchCapabilities{}View on GitHub (pinned to 806f444784)
Solutions
- Inspect the underlying err (via errors.Unwrap / %w chain) for the exact position or token that failed Finalize
- Fix the filter expression: correct the field name, operator, and value type in the client request
- Validate the filter client-side before sending, mirroring expression.Finalize rules
- Confirm the structured-filters feature gate is enabled and the filter is meant to stand alone (EnsureFilterStandsAlone)
Example fix
// before
curl ... -d '{"filter":{"field":"dur","op":"~","value":"fast"}}'
// after
curl ... -d '{"filter":{"field":"duration","op":">=","value":"100ms"}}' Defensive patterns
Strategy: validation
Validate before calling
// Client-side: validate the filter before sending
func validFilter(f map[string]any) bool {
field, _ := f["field"].(string)
op, _ := f["op"].(string)
_, hasVal := f["value"]
return field != "" && op != "" && hasVal
}
if !validFilter(req.Filter) {
return errors.New("filter requires field, op, and value")
} Try / catch
// Go: check the sentinel error
if err := qs.FindTraceIDs(ctx, query); err != nil {
if errors.Is(err, tracestore.ErrFilterInvalid) {
return http.StatusBadRequest // filter, not server, is at fault
}
return err
} Prevention
- Mirror expression.Finalize validation rules in client code before sending filters
- Use typed clients/SDK builders for structured filters instead of raw JSON
- Pin API client and jaeger-query versions so filter semantics match
- Test filters against a local all-in-one instance before production
When it happens
Trigger: A client sends a search request with query.Filter set (structured filters feature gate enabled) whose expression fails Finalize — e.g. malformed tag/field references, invalid operators, or a value that does not match the expected type of the attribute.
Common situations: API clients hand-crafting filter JSON instead of using a typed client; a Jaeger UI or downstream tool emitting filters against attribute names/types that changed between backend versions; proxies forwarding filters written for a different Jaeger version.
Related errors
- access denied
- no sampling strategy provider specified, expecting 'adaptive
- only one sampling strategy provider can be specified, 'adapt
- reload interval must be a positive value, or zero to disable
- jaeger exporter is no longer supported, please use otlp
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/47c22cd58013a537.
Report an issue: GitHub.