grpc/grpc-java · error · IllegalArgumentException

Invalid input: expected trace ID at offset

Error message

Invalid input: expected trace ID at offset ${pos}

What it means

BinaryFormat.parseBytes deserializes a SpanContext from the OpenTelemetry binary propagation format, which expects a version byte followed by the trace-ID field (field ID 0) with 16 bytes. This IllegalArgumentException is thrown when the byte at the trace-ID position is not the expected TRACE_ID_FIELD_ID, meaning the serialized bytes do not conform to the expected propagation format.

Solutions

  1. Verify the incoming bytes were produced by the same OpenTelemetry binary format (version byte 0x00, then field ID 0x00 + 16-byte trace ID, then field ID 0x01 + 8-byte span ID, then optional trace-flags field).
  2. Check that the upstream service uses the OpenTelemetry binary propagator, not B3 or another format; align propagators on both sides.
  3. Inspect the raw bytes (hex dump) at offset 1 to confirm the field ID and that the array is at least 29 bytes long.
  4. Wrap parseBytes in try-catch for IllegalArgumentException and fall back to treating the header as absent (no valid span context).

Example fix

// before
SpanContext ctx = BinaryFormat.parseBytes(headerBytes);
// after
SpanContext ctx;
try {
  ctx = BinaryFormat.parseBytes(headerBytes);
} catch (IllegalArgumentException e) {
  ctx = SpanContext.getInvalid();
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean isPlausibleOtelBinary(byte[] b) {
  return b != null && b.length >= 1 && b[0] == 0 && b.length >= 29 && b[1] == 0;
}

Try / catch

try {
  ctx = BinaryFormat.parseBytes(bytes);
} catch (IllegalArgumentException e) {
  ctx = SpanContext.getInvalid();
}

Prevention

When it happens

Trigger: Calling BinaryFormat.parseBytes (via the OpenTelemetry propagator's extract, e.g. SpanContext.fromByteArray or carrier extraction) with bytes whose second byte is not TRACE_ID_FIELD_ID (0x00) — i.e. a malformed, corrupted, or foreign-format binary span context.

Common situations: Interoperating with a service that emits a different binary propagation format (e.g. B3 binary or W3C traceparent encoded differently), bytes truncated to only the version byte, manual byte-array construction for tests, or an outdated/incompatible version of the propagator on the other side of the wire.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/1ae578bcc7a009e9. Report an issue: GitHub.

Appendix: source

Thrown at opentelemetry/src/main/java/io/grpc/opentelemetry/BinaryFormat.java:126

  @Override
  public SpanContext parseBytes(byte[] serialized) {
    checkNotNull(serialized, "bytes");
    if (serialized.length == 0 || serialized[0] != VERSION_ID) {
      throw new IllegalArgumentException("Unsupported version.");
    }
    if (serialized.length < REQUIRED_FORMAT_LENGTH) {
      throw new IllegalArgumentException("Invalid input: truncated");
    }
    String traceId;
    String spanId;
    TraceFlags traceFlags = TraceFlags.getDefault();
    int pos = 1;
    if (serialized[pos] == TRACE_ID_FIELD_ID) {
      traceId = TraceId.fromBytes(
          Arrays.copyOfRange(serialized, pos + ID_SIZE, pos + ID_SIZE + TRACE_ID_SIZE));
      pos += ID_SIZE + TRACE_ID_SIZE;
    } else {
      throw new IllegalArgumentException("Invalid input: expected trace ID at offset " + pos);
    }
    if (serialized[pos] == SPAN_ID_FIELD_ID) {
      spanId = SpanId.fromBytes(
          Arrays.copyOfRange(serialized, pos + ID_SIZE, pos + ID_SIZE + SPAN_ID_SIZE));
      pos += ID_SIZE + SPAN_ID_SIZE;
    } else {
      throw new IllegalArgumentException("Invalid input: expected span ID at offset " + pos);
    }
    if (serialized.length > pos && serialized[pos] == TRACE_FLAG_FIELD_ID) {
      if (serialized.length < ALL_FORMAT_LENGTH) {
        throw new IllegalArgumentException("Invalid input: truncated");
      }
      traceFlags = TraceFlags.fromByte(serialized[pos + ID_SIZE]);
    }
    return SpanContext.create(traceId, spanId, traceFlags, TraceState.getDefault());
  }
}

View on GitHub (pinned to 64daddc1f3)