openzipkin/zipkin · error · IllegalArgumentException

hex field greater than 32 chars long: {}

Error message

hex field greater than 32 chars long: {}

What it means

Zipkin IDs (trace/span/parent) are at most 16 bytes, i.e. 32 hex characters. ReadBuffer.readBytesAsHex converts fixed-length byte fields to hex and throws 'hex field greater than 32 chars long: N' when a length-delimited ID field in the payload is longer than 16 bytes — the decoder refuses impossible IDs rather than truncating.

Source

Thrown at zipkin/src/main/java/zipkin2/internal/ReadBuffer.java:308

  abstract int pos();

  abstract short readShort();

  abstract int readInt();

  abstract long readLong();

  abstract long readLongLe();

  @Override public final int read() {
    return available() > 0 ? readByteUnsafe() : -1;
  }

  final String readBytesAsHex(int length) {
    // All our hex fields are at most 32 characters.
    if (length > 32) {
      throw new IllegalArgumentException("hex field greater than 32 chars long: " + length);
    }

    require(length);
    char[] result = RecyclableBuffers.shortStringBuffer();
    int hexLength = length * 2;
    for (int i = 0; i < hexLength; i += 2) {
      byte b = readByteUnsafe();
      result[i + 0] = HEX_DIGITS[(b >> 4) & 0xf];
      result[i + 1] = HEX_DIGITS[b & 0xf];
    }
    return new String(result, 0, hexLength);
  }

  /**
   * @return the value read. Use {@link WriteBuffer#varintSizeInBytes(long)} to tell how many bytes.
   * @throws IllegalArgumentException if more than 64 bits were encoded
   */
  // included in the main api as this is used commonly, for example reading proto tags

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. On the producer, convert hex IDs to their 16-byte binary form before putting them into proto3/thrift fields (Zipkin never transports ID hex in binary fields).
  2. Verify the length-delimited prefix of the ID field is exactly 16 (0x10) for a 128-bit ID or 8 for 64-bit.
  3. Check the bytes are proto3 and not hex-encoded JSON IDs pasted into a binary field.
  4. Drop the malformed span and log the offending ID bytes for the producer team.

Example fix

// before
// sent the 32-char hex string as bytes (32 bytes)
field.write(b, span.traceId().getBytes(UTF_8));

// after
// send the 16-byte binary form; hex conversion happens on read
byte[] id = BufferUtil.lowerHexToUnsignedBytes(span.traceId());
field.write(b, id);
Defensive patterns

Strategy: validation

Validate before calling

// producer-side: verify ID hex length before binary conversion
byte[] toIdBytes(String hex) {
  if (hex.length() != 16 && hex.length() != 32) throw new IllegalArgumentException("bad id: " + hex);
  return zipkin2.internal.BufferUtil.lowerHexToUnsignedBytes(hex);
}

Type guard

boolean isValidTraceIdHex(String s) { return s != null && s.matches("[0-9a-f]{16}([0-9a-f]{16})?"); }

Prevention

When it happens

Trigger: PROTO3 (or thrift) decoding where a trace_id/span_id field carries more than 16 bytes — e.g. a 32-byte ID mistakenly sent as raw bytes instead of the 16-byte binary form, or a misread length prefix on an ID field.

Common situations: Producers that treat the 32-char hex string as bytes (32 bytes instead of 16); systems generating non-Zipkin 128-bit-plus identifiers; payload corruption shifting a length prefix.

Related errors


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