openzipkin/zipkin · error · IllegalArgumentException

maxDuration is only valid with minDuration

Error message

maxDuration is only valid with minDuration

What it means

Thrown by QueryRequest.Builder.build() when maxDuration is set while minDuration is null. Zipkin's query model treats maxDuration only as the upper bound of a duration filter whose lower bound is minDuration; an upper bound alone is not a supported query, so the builder rejects the combination.

Source

Thrown at zipkin/src/main/java/zipkin2/storage/QueryRequest.java:259

      // coerce service and span names to lowercase
      if (serviceName != null) serviceName = serviceName.toLowerCase(Locale.ROOT);
      if (remoteServiceName != null) remoteServiceName = remoteServiceName.toLowerCase(Locale.ROOT);
      if (spanName != null) spanName = spanName.toLowerCase(Locale.ROOT);

      if ("".equals(serviceName)) serviceName = null;
      if ("".equals(remoteServiceName)) remoteServiceName = null;
      if ("".equals(spanName) || "all".equals(spanName)) spanName = null;

      if (endTs <= 0) throw new IllegalArgumentException("endTs <= 0");
      if (limit <= 0) throw new IllegalArgumentException("limit <= 0");
      if (lookback <= 0) throw new IllegalArgumentException("lookback <= 0");
      if (minDuration != null) {
        if (minDuration <= 0) throw new IllegalArgumentException("minDuration <= 0");
        if (maxDuration != null && maxDuration < minDuration) {
          throw new IllegalArgumentException("maxDuration < minDuration");
        }
      } else if (maxDuration != null) {
        throw new IllegalArgumentException("maxDuration is only valid with minDuration");
      }

      return new QueryRequest(
        serviceName,
        remoteServiceName,
        spanName,
        annotationQuery,
        minDuration,
        maxDuration,
        endTs,
        lookback,
        limit
      );
    }

    Builder() {
    }
  }

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Set a minDuration whenever you set maxDuration, e.g. .minDuration(1).maxDuration(500000)
  2. If you only need an upper bound, keep minDuration very small (1 microsecond) rather than omitting it
  3. Guard at the call site: skip both duration filters unless minDuration is present

Example fix

// before
QueryRequest.newBuilder().endTs(endTs).limit(10)
    .maxDuration(60000000L) // throws: only valid with minDuration
    .build();

// after
QueryRequest.newBuilder().endTs(endTs).limit(10)
    .minDuration(1L)
    .maxDuration(60000000L)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

// maxDuration requires minDuration; substitute a minimal lower bound when only max is set
Long effectiveMin = (minDuration != null) ? minDuration
                     : (maxDuration != null ? 1L : null);
QueryRequest.Builder b = QueryRequest.newBuilder().endTs(endTs).limit(10);
if (effectiveMin != null) b.minDuration(effectiveMin);
if (maxDuration != null) b.maxDuration(maxDuration);

Try / catch

try {
    builder.maxDuration(maxDurationMicros).build();
} catch (IllegalArgumentException e) {
    // contract violation: set minDuration(1L) or drop maxDuration, then rebuild once
    if ("maxDuration is only valid with minDuration".equals(e.getMessage())) {
        builder.minDuration(1L).build();
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling .maxDuration(500000) without ever calling .minDuration(...); conditionally setting minDuration (e.g. only when a checkbox is ticked) while always setting maxDuration; clearing minDuration to null in shared builder code but leaving maxDuration set.

Common situations: A UI where users can set 'max duration' alone and the backend forwards it verbatim; refactoring that removed the minDuration call but kept maxDuration; copying a query template and deleting the minDuration line.

Related errors


AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14). Data as JSON: /api/errors/adbddf34f141e293. Report an issue: GitHub.