jaegertracing/jaeger · error

%w: it can carry only one predicate on %q

Error message

%w: it can carry only one predicate on %q

What it means

Each legacy scalar field holds a single value: one entry per attribute key, one ServiceName, one OperationName, one DurationMin and one DurationMax. When the filter's flat conjunction contains two predicates on the same field — the same attribute key twice, two service-name equalities, two min bounds — the second cannot be stored, so applyAttribute/applyText/applyDurationBound refuse it with ErrFilterUnsupported naming the field. Note this occurs even when the two predicates are identical, since AND-ing equal predicates still needs two slots.

Source

Thrown at internal/storage/v2/api/tracestore/shape.go:295

func errNotADuration(name string, value expression.Expression) error {
	if constant, ok := value.(*expression.AnyValue); ok {
		return fmt.Errorf("%w: the bound %q on %q was never read as a duration",
			ErrFilterInvalid, constant.Value, name)
	}
	return fmt.Errorf(`%w: it compares %q against a duration such as "2s" only`,
		ErrFilterUnsupported, name)
}

func errUnsupportedOperator(op expression.Operator) error {
	return fmt.Errorf("%w: it does not support the operator %q", ErrFilterUnsupported, op)
}

func errUnsupportedOperatorOn(op expression.Operator, name string) error {
	return fmt.Errorf("%w: it does not support the operator %q on %q", ErrFilterUnsupported, op, name)
}

func errRepeatedPredicateOn(name string) error {
	return fmt.Errorf("%w: it can carry only one predicate on %q", ErrFilterUnsupported, name)
}

View on GitHub (pinned to 806f444784)

Solutions

  1. Deduplicate predicates on the same field before building the filter; keep only one per field/key.
  2. Express multiple allowed values via an IN-style construct or issue one query per value and merge results.
  3. Target a backend with native filter support where repeated predicates are conjoined natively.

Example fix

// before
preds := []Expression{eq("service.name","a"), eq("service.name","a")} // duplicate
// after — dedupe before building the conjunction
seen := map[string]bool{}; var unique []Expression
for _, p := range preds { key := refKeyOf(p); if !seen[key] { seen[key] = true; unique = append(unique, p) } }
Defensive patterns

Strategy: validation

Validate before calling

func dedupePredicates(preds []*expression.Call) []*expression.Call {
  seen := map[string]bool{}; out := preds[:0]
  for _, p := range preds {
    key := fmt.Sprintf("%T|%v", p.Args[0], p.Args[0])
    k2 := refKey(p) // e.g. attribute key or level+name
    if seen[k2] { continue }
    seen[k2] = true; out = append(out, p)
  }
  return out
}

Try / catch

legacy, err := params.ToLegacyShape()
if errors.Is(err, tracestore.ErrFilterUnsupported) {
  return nil, fmt.Errorf("only one predicate per field is supported: %w", err)
}

Prevention

When it happens

Trigger: ToLegacyShape() processes a conjunction containing two OpEq predicates on the same attribute key, two equalities on span.name or resource.service, or two OpGte (or two OpLte) bounds on span.duration.

Common situations: A UI accumulates duplicate filter chips without deduplication; query builders append the same condition twice; users express name in ('a','b') as two name == predicates, which OR semantics cannot map onto one legacy slot.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/1f4ad8e81f91ecc8. Report an issue: GitHub.