openzipkin/zipkin · error · IllegalArgumentException

empty trace ID

Error message

empty trace ID

What it means

Span.Builder.traceId(long high, long low) encodes a 64- or 128-bit trace ID from two longs and throws IllegalArgumentException('empty trace ID') when both high and low are zero. A trace ID of all zeros is invalid in the Zipkin/B3 data model because it cannot uniquely identify a trace. The guard runs before any hex encoding happens, so the builder state is left untouched.

Source

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

    }

    /**
     * Sets {@link Span#id()} or throws {@link IllegalArgumentException} if not lower-hex format.
     */
    public Builder traceId(String traceId) {
      this.traceId = normalizeTraceId(traceId);
      return this;
    }

    /**
     * Encodes 64 or 128 bits from the input into a hex trace ID.
     *
     * @param high Upper 64bits of the trace ID. Zero means the trace ID is 64-bit.
     * @param low Lower 64bits of the trace ID.
     * @throws IllegalArgumentException if both values are zero
     */
    public Builder traceId(long high, long low) {
      if (high == 0L && low == 0L) throw new IllegalArgumentException("empty trace ID");
      char[] data = RecyclableBuffers.shortStringBuffer();
      int pos = 0;
      if (high != 0L) {
        writeHexLong(data, pos, high);
        pos += 16;
      }
      writeHexLong(data, pos, low);
      this.traceId = new String(data, 0, high != 0L ? 32 : 16);
      return this;
    }

    /** Hex encodes the input as the {@link Span#parentId()} or unsets if the input is zero. */
    public Builder parentId(long parentId) {
      this.parentId = parentId != 0L ? toLowerHex(parentId) : null;
      return this;
    }

    /**

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Skip creating the span when both longs are zero: treat zero as 'no active trace' rather than passing it through.
  2. Trace the origin of the two longs (e.g. B3 single-header or traceparent parsing) and fix the upstream parser so a missing ID is surfaced as null/absent, not 0.
  3. If the ID arrives as a hex string, use Span.Builder.traceId(String) or Span.normalizeTraceId instead of converting to longs yourself.

Example fix

// before
span = Span.newBuilder().traceId(high, low).id(spanId).build();

// after
if (high == 0L && low == 0L) return null; // no active trace
span = Span.newBuilder().traceId(high, low).id(spanId).build();
Defensive patterns

Strategy: validation

Validate before calling

boolean hasTraceId(long high, long low) {
  return high != 0L || low != 0L;
}
// use: if (hasTraceId(high, low)) b.traceId(high, low); else startNewTrace();

Prevention

When it happens

Trigger: Calling Span.newBuilder().traceId(0L, 0L), or traceId(high, low) where both values came from an uninitialized long field, a defaulted primitive, or a parsed value that silently defaulted to 0 on error.

Common situations: Instrumentation code that reads trace IDs from headers/context objects that were missing (so the parsed long stayed 0), porting code from a tracer that represents 'no trace' as zero, or test fixtures generated with new Random() seeds of 0.

Related errors


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