openzipkin/zipkin · error · IllegalArgumentException

empty id

Error message

empty id

What it means

Span.Builder.id(long id) hex-encodes a 64-bit span ID and throws IllegalArgumentException('empty id') when the value is 0L. A span ID of zero is reserved/invalid in the Zipkin model, exactly like an all-zero hex ID. Every span must have a non-zero ID so it can be uniquely referenced as a parent by child spans.

Source

Thrown at zipkin/src/main/java/zipkin2/Span.java:448

        return this;
      }
      int length = parentId.length();
      if (length == 0) throw new IllegalArgumentException("parentId is empty");
      if (length > 16) throw new IllegalArgumentException("parentId.length > 16");
      if (validateHexAndReturnZeroPrefix(parentId) == length) {
        this.parentId = null;
      } else {
        this.parentId = length < 16 ? padLeft(parentId, 16) : parentId;
      }
      return this;
    }

    /**
     * Hex encodes the input as the {@link Span#id()} or throws IllegalArgumentException if the
     * input is zero.
     */
    public Builder id(long id) {
      if (id == 0L) throw new IllegalArgumentException("empty id");
      this.id = toLowerHex(id);
      return this;
    }

    /** Sets {@link Span#id()} or throws {@link IllegalArgumentException} if not lower-hex format. */
    public Builder id(String id) {
      if (id == null) throw new NullPointerException("id == null");
      int length = id.length();
      if (length == 0) throw new IllegalArgumentException("id is empty");
      if (length > 16) throw new IllegalArgumentException("id.length > 16");
      if (validateHexAndReturnZeroPrefix(id) == 16) {
        throw new IllegalArgumentException("id is all zeros");
      }
      this.id = length < 16 ? padLeft(id, 16) : id;
      return this;
    }

    /** Sets {@link Span#kind} */

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Ensure the ID is generated with a non-zero generator (e.g. ThreadLocalRandom.current().nextLong() retried on 0, or the tracer's built-in ID generator).
  2. If the ID genuinely may be absent, skip creating the span instead of defaulting the long to 0.
  3. If you hold the ID as a hex string, use .id(String) which validates the hex form directly.

Example fix

// before
long spanId = context.spanId(); // may be 0 when absent
b.id(spanId);

// after
long spanId = context.spanId();
if (spanId == 0L) spanId = ThreadLocalRandom.current().nextLong();
b.id(spanId);
Defensive patterns

Strategy: validation

Validate before calling

long nonZeroSpanId(long candidate) {
  return candidate != 0L ? candidate : ThreadLocalRandom.current().nextLong();
}
// b.id(nonZeroSpanId(ctx.spanId()));

Prevention

When it happens

Trigger: Calling .id(0L) directly, or passing a long that was never assigned because the ID source (MDC, context, random generator result assigned conditionally) was missing.

Common situations: Custom instrumentation that generates span IDs with new Random().nextLong() and forgets that nextLong() can return 0 (rare but real), or code paths that read a span ID from a context object that was not initialized for this request.

Related errors


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