openzipkin/zipkin · error · IllegalArgumentException

calls were empty

Error message

calls were empty

What it means

AggregateCall.newVoidCall(List<Call<Void>>) aggregates zero or more void calls and throws IllegalArgumentException('calls were empty') when the list is empty — an aggregate with nothing to wait on has no defined success value (its execute() would have to block on nothing or return a fabricated result). With exactly one call it returns that call directly; only 2+ calls are wrapped.

Source

Thrown at zipkin/src/main/java/zipkin2/internal/AggregateCall.java:27

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import zipkin2.Call;
import zipkin2.Callback;

/**
 * A call that blocks on others to complete before invoking a callback or returning from {@link
 * #execute()}. The first error will be returned upstream, later ones will be suppressed.
 *
 * @param <I> the type of returned from {@link Call#execute()}
 * @param <O> the type representing the aggregate success value
 */
public abstract class AggregateCall<I, O> extends Call.Base<O> {

  public static Call<Void> newVoidCall(List<Call<Void>> calls) {
    if (calls.isEmpty()) throw new IllegalArgumentException("calls were empty");
    if (calls.size() == 1) return calls.get(0);
    return new AggregateVoidCall(calls);
  }

  static final class AggregateVoidCall extends AggregateCall<Void, Void> {
    AggregateVoidCall(List<Call<Void>> calls) {
      super(calls);
    }

    @Override protected Void newOutput() {
      return null;
    }

    @Override protected void append(Void input, Void output) {
    }

    @Override public AggregateVoidCall clone() {
      return new AggregateVoidCall(cloneCalls());

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Guard for the empty case before aggregating: return a no-op Call (e.g. Call.create(null)) instead of calling newVoidCall.
  2. Skip issuing the aggregate operation entirely when the batch is empty.
  3. Ensure upstream batching cannot deliver empty lists (reporters should not flush zero spans).

Example fix

// before
return AggregateCall.newVoidCall(calls); // calls may be []

// after
return calls.isEmpty() ? Call.create(null) : AggregateCall.newVoidCall(calls);
Defensive patterns

Strategy: validation

Validate before calling

Call<Void> aggregate = calls.isEmpty()
    ? Call.create(null)
    : AggregateCall.newVoidCall(calls);

Prevention

When it happens

Trigger: Building a storage-layer bulk operation (e.g. SpanStore accepts/aggregates) from a fan-out list that happens to be empty — say, zero client addresses to check or an empty consumer batch passed to a compound call.

Common situations: Custom zipkin-server storage backends or collectors that aggregate per-index calls (Elasticsearch/Cassandra-style fan-out) and hit empty batches during low traffic or after filtering removes all items.

Related errors


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