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

  1. Pass endTs as epoch milliseconds (e.g. System.currentTimeMillis()); lookback in millis too.
  2. Validate request parameters at the API edge: reject endTs<=0 with 400 before calling storage.
  3. In tests, fix the clock or pass explicit positive timestamps.
  4. 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

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


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