openzipkin/zipkin · error · IllegalArgumentException
endTs <= 0
Error message
endTs <= 0
What it means
InMemoryStorage.getDependencies(endTs, lookback) computes dependency links for a time window ending at endTs; endTs is epoch milliseconds and must be positive. Zero or negative throws IllegalArgumentException('endTs <= 0') (lookback gets an equivalent check). It is a fail-fast guard against nonsensical time windows, often exposing a units bug.
Source
Thrown at zipkin/src/main/java/zipkin2/storage/InMemoryStorage.java:419
return Call.create(new ArrayList<>(serviceToTraceIds.keySet()));
}
@Override public synchronized Call<List<String>> getRemoteServiceNames(String service) {
if (service.isEmpty() || !searchEnabled) return Call.emptyList();
service = service.toLowerCase(Locale.ROOT); // service names are always lowercase!
return Call.create(
new ArrayList<>(serviceToRemoteServiceNames.get(service)));
}
@Override public synchronized Call<List<String>> getSpanNames(String service) {
if (service.isEmpty() || !searchEnabled) return Call.emptyList();
service = service.toLowerCase(Locale.ROOT); // service names are always lowercase!
return Call.create(new ArrayList<>(serviceToSpanNames.get(service)));
}
@Override
public synchronized Call<List<DependencyLink>> getDependencies(long endTs, long lookback) {
if (endTs <= 0) throw new IllegalArgumentException("endTs <= 0");
if (lookback <= 0) throw new IllegalArgumentException("lookback <= 0");
Set<String> lowTraceIdsInRange =
lowTraceIdsInRange(spansByTraceIdTimestamp.keySet(), endTs, lookback);
List<DependencyLink> links = getDependencyLinks(lowTraceIdsInRange);
return Call.create(links);
}
// We don't have a query parameter for strictTraceId when fetching dependency links, so we
// ignore traceIdHigh. Otherwise, a single trace can appear as two, doubling callCount.
List<DependencyLink> getDependencyLinks(Set<String> lowTraceIdsInRange) {
if (lowTraceIdsInRange.isEmpty()) return Collections.emptyList();
DependencyLinker linksBuilder = new DependencyLinker();
for (String lowTraceId : lowTraceIdsInRange) {
linksBuilder.putTrace(spansByTraceId(lowTraceId));
}
return linksBuilder.link();
}View on GitHub (pinned to 878ce2a1fa)
Solutions
- Pass endTs as epoch milliseconds (e.g. System.currentTimeMillis()); lookback in millis too.
- Validate request parameters at the API edge: reject endTs<=0 with 400 before calling storage.
- In tests, fix the clock or pass explicit positive timestamps.
- Check for request models where endTs is a primitive long defaulting to 0 when the client omits it.
Example fix
// before long endTs = request.getEndTs(); // defaults to 0 when client omits it storage.getDependencies(endTs, lookback); // after long endTs = request.getEndTs() > 0 ? request.getEndTs() : System.currentTimeMillis(); storage.getDependencies(endTs, lookback);
Defensive patterns
Strategy: validation
Validate before calling
if (endTs <= 0 || lookback <= 0) throw new ResponseStatusException(BAD_REQUEST, "endTs and lookback must be positive epoch millis");
Type guard
boolean isValidTimeWindow(long endTs, long lookback) { return endTs > 0 && lookback > 0; } Prevention
- Standardize on epoch milliseconds end-to-end.
- Never forward primitive-long request defaults (0) into storage calls.
When it happens
Trigger: Calling storage.getDependencies(0, lookback) or with a negative endTs — e.g. passing epoch *seconds* multiplied wrongly, an unset default long (0), or System.currentTimeMillis() mocked to 0 in tests.
Common situations: Calling the dependency endpoint /dependencies?endTs=... with a non-numeric/blank value that parses to 0; unit confusion (seconds vs millis); request DTO defaults of 0L forwarded without validation.
Related errors
- Start bucket ({startBucket}) > end bucket ({endBucket})
- endTs <= 0
- endTs <= 0
- limit <= 0
- input is not a list
AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14).
Data as JSON: /api/errors/d70835f69b9892b7.
Report an issue: GitHub.