pinpoint-apm/pinpoint · error · IllegalArgumentException

invalid spanId: ${error}

Error message

invalid spanId: ${error}

What it means

OtlpIdValidator.validateSpanId validates an OTLP spanId ByteString: it must be non-empty, exactly 8 bytes, and not all zeros; on failure it throws IllegalArgumentException with the specific reason. Valid span IDs are required since they become the span's storage key.

Source

Thrown at otlptrace/otlptrace-collector/src/main/java/com/navercorp/pinpoint/otlp/trace/collector/mapper/OtlpIdValidator.java:83

     * @return the 16-byte trace ID
     * @throws IllegalArgumentException if empty, not 16 bytes, or all-zero
     */
    public static byte[] validateTraceId(ByteString traceId) {
        final String error = traceIdError(traceId);
        if (error != null) {
            throw new IllegalArgumentException("invalid traceId: " + error);
        }
        return traceId.toByteArray();
    }

    /**
     * @return the span ID as a big-endian long
     * @throws IllegalArgumentException if empty, not 8 bytes, or all-zero
     */
    public static long validateSpanId(ByteString spanId) {
        final String error = spanIdError(spanId);
        if (error != null) {
            throw new IllegalArgumentException("invalid spanId: " + error);
        }
        return ByteStringUtils.parseLong(spanId);
    }

    private static String traceIdError(ByteString traceId) {
        return idError(traceId, TRACE_ID_LEN);
    }

    private static String spanIdError(ByteString spanId) {
        return idError(spanId, SPAN_ID_LEN);
    }

    private static String idError(ByteString id, int expectedLen) {
        if (ByteStringUtils.isEmpty(id)) {
            return "empty";
        }
        if (id.size() != expectedLen) {
            return "length " + id.size() + " (expected " + expectedLen + ")";

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Generate 8 random bytes for every exported span's spanId using the official OpenTelemetry SDK.
  2. Never export spans with empty or all-zero spanIds — filter them out before calling the collector.
  3. When bridging IDs, pad/truncate to exactly 8 bytes and verify they are not all zero.
  4. Catch IllegalArgumentException around span mapping and skip/log the invalid span rather than rejecting the batch.

Example fix

// before
Span.newBuilder().setSpanId(ByteString.EMPTY)... // empty id -> throws
// after
byte[] spanId = new byte[8];
ThreadLocalRandom.current().nextBytes(spanId);
Span.newBuilder().setSpanId(ByteString.copyFrom(spanId))...
Defensive patterns

Strategy: validation

Validate before calling

boolean validSpanId(byte[] id) {
    return id != null && id.length == 8 && !Arrays.equals(id, new byte[8]);
}

Type guard

static boolean isUsableSpanId(ByteString spanId) {
    return spanId != null
        && spanId.size() == 8
        && !spanId.equals(ByteString.copyFrom(new byte[8]));
}

Try / catch

try {
    long spanId = OtlpIdValidator.validateSpanId(span.getSpanId());
} catch (IllegalArgumentException e) {
    log.warn("skipping span with bad spanId: {}", e.getMessage());
    continue; // skip span, continue batch
}

Prevention

When it happens

Trigger: Exporting spans with an empty spanId, a spanId that is not 8 bytes long, or all-zero bytes (OTLP's invalid-id sentinel), typically from hand-constructed Span messages or bridged telemetry.

Common situations: Test fixtures using ByteString.EMPTY; bridges from systems with different span-id widths; code that sets spanId from a parsed hex string shorter than 16 hex chars; SDKs producing zeroed IDs for unsampled spans that were exported anyway.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/5af1b62af69a718c. Report an issue: GitHub.