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
- Set a minDuration whenever you set maxDuration, e.g. .minDuration(1).maxDuration(500000)
- If you only need an upper bound, keep minDuration very small (1 microsecond) rather than omitting it
- 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
- Encapsulate the pairing rule in one query-builder wrapper so callers cannot set max without min
- Document in your API that maxDuration alone is unsupported
- Assert the invariant (max set implies min set) before calling build()
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
- minDuration <= 0
- maxDuration < minDuration
- concurrency < 1
- rate should be between 0 and 1: was {}
- limit <= 0
AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14).
Data as JSON: /api/errors/adbddf34f141e293.
Report an issue: GitHub.