jaegertracing/jaeger · error · ErrInterceptorFilter
%w: %w
Error message
%w: %w
What it means
ErrInterceptorFilter is wrapped when expression.Finalize fails on the filter returned by a query interceptor. Finalize validates and normalizes the expression; a failure means the interceptor produced a structurally invalid or ill-typed filter that Jaeger cannot compile into a storage query. The original Finalize error is chained via Go's multi-%w wrapping for errors.Is/As inspection.
Source
Thrown at cmd/jaeger/internal/extension/jaegerquery/querysvc/interceptor.go:111
// not hand to storage. An interceptor builds its filter by hand, in code jaeger-query does not
// control, and a malformed tree is typically answered by a backend matching nothing rather than
// refusing — so a search meant to be narrowed would come back wrong with nothing to say why.
//
// It finalizes rather than only validates, so that an interceptor's predicate reaches storage as the
// equal of one a caller sent, having been through the same stage.
//
// A nil filter here is the one mistake that fails open: a search that arrived with predicates and
// leaves with none asks for every trace in the time range. Its caller returns before this point when
// there were no predicates to begin with, since a caller may legitimately search a time range and
// nothing else.
func finalizeInterceptorFilter(returned *expression.Call) (*expression.Call, error) {
if returned == nil {
return nil, fmt.Errorf("%w: it returned no filter for a query that had predicates, which "+
"would widen the search to every trace in the time range", ErrInterceptorFilter)
}
finalized, err := expression.Finalize(returned)
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrInterceptorFilter, err)
}
return finalized, nil
}
// interceptResults hands every batch of seq to the interceptors' OnResult in order, threading the
// context each returns into the next so that state can accumulate across a multi-batch result.
// An OnResult error ends the stream rather than yielding later batches, which could leak results
// the failed sanitize or redaction was meant to withhold.
//
// It wraps the batches as storage yielded them, before the query service aggregates and adjusts
// them, so an interceptor rewrites the traces the reader actually returned.
func (qs QueryService) interceptResults(
ctx context.Context,
seq iter.Seq2[[]ptrace.Traces, error],
) iter.Seq2[[]ptrace.Traces, error] {
if len(qs.options.Interceptors) == 0 {
return seq
}View on GitHub (pinned to 806f444784)
Solutions
- Inspect the chained Finalize error in the message for the exact structural problem.
- Fix the interceptor to build the filter using the expression package's public constructors only.
- Have the interceptor validate its expression (Finalize) itself before returning it to fail fast locally.
- Rebuild against the jaeger version in use if the expression API changed underneath.
Example fix
// before
return expression.NewCall(customFn, operandOfWrongType), nil
// after
return expression.NewCall(expression.EqFn, expression.NewStringField("service.name"), expression.NewStringLiteralValue("foo")), nil Defensive patterns
Strategy: try-catch
Validate before calling
// inside interceptor before returning
if _, err := expression.Finalize(myCall); err != nil {
return nil, fmt.Errorf("interceptor built invalid expression: %w", err)
} Try / catch
if err != nil && errors.Is(err, querysvc.ErrInterceptorFilter) {
var finErr error
if errors.As(err, &finErr) {
log.Printf("finalize failed inside interceptor filter: %v", finErr)
}
return err
} Prevention
- Build expressions only with the expression package's typed constructors.
- Self-validate interceptor output with Finalize in its own tests.
- Keep interceptor and jaeger versions in lockstep.
When it happens
Trigger: finalizeInterceptorFilter calls expression.Finalize(returned) on the interceptor's returned *expression.Call and Finalize returns an error — e.g. the interceptor built a Call with a wrong argument count, an unknown function, or mismatched operand types.
Common situations: Custom interceptor composing expression AST nodes by hand with incorrect types or arity; an interceptor forwarding a stale/partially built expression; version skew where the expression API changed shape between Jaeger versions.
Related errors
- %w: it returned no filter for a query that had predicates, w
- wrong number of arguments
- only one of basic auth (--es.username/--es.password), --es.t
- invalid query filter
- cannot send the query filter: %w
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/81342b9a463636b0.
Report an issue: GitHub.