openzipkin/zipkin · error · IllegalArgumentException

should be lower-hex encoded with no prefix

Error message

 should be lower-hex encoded with no prefix

What it means

Span.validateHexAndReturnZeroPrefix(String) throws IllegalArgumentException('<id> should be lower-hex encoded with no prefix') when any character is outside [0-9a-f]. Uppercase hex (A-F), '0x' prefixes, and separators like '-' all fail — Zipkin IDs are strictly lower-hex. This helper backs id(String), parentId(String), and normalizeTraceId, so the exception surfaces from those setters.

Source

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

    writeHexByte(data, pos + 6, (byte) ((v >>> 32L) & 0xff));
    writeHexByte(data, pos + 8, (byte) ((v >>> 24L) & 0xff));
    writeHexByte(data, pos + 10, (byte) ((v >>> 16L) & 0xff));
    writeHexByte(data, pos + 12, (byte) ((v >>> 8L) & 0xff));
    writeHexByte(data, pos + 14, (byte) (v & 0xff));
  }

  static void writeHexByte(char[] data, int pos, byte b) {
    data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf];
    data[pos + 1] = HEX_DIGITS[b & 0xf];
  }

  static int validateHexAndReturnZeroPrefix(String id) {
    int zeros = 0;
    boolean inZeroPrefix = id.charAt(0) == '0';
    for (int i = 0, length = id.length(); i < length; i++) {
      char c = id.charAt(i);
      if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) {
        throw new IllegalArgumentException(id + " should be lower-hex encoded with no prefix");
      }
      if (c != '0') {
        inZeroPrefix = false;
      } else if (inZeroPrefix) {
        zeros++;
      }
    }
    return zeros;
  }

  static <T extends Comparable<? super T>> List<T> sortedList(@Nullable List<T> in) {
    if (in == null || in.isEmpty()) return Collections.emptyList();
    if (in.size() == 1) return Collections.singletonList(in.get(0));
    Object[] array = in.toArray();
    Arrays.sort(array);

    // dedupe
    int j = 0, i = 1;

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Normalize before setting: trim, strip any '0x' prefix, remove separators, and lowercase the value.
  2. Map UUIDs by removing dashes and lowercasing (a 32-char UUID maps directly to a 128-bit trace ID).
  3. Validate at ingestion with a [0-9a-f]{1,32} regex and reject/repair non-conforming IDs.

Example fix

// before
b.traceId(uuid.toString()); // "4BF1F00C-E581-..." uppercase + dashes

// after
b.traceId(uuid.toString().replace("-", "").toLowerCase(Locale.ROOT));
Defensive patterns

Strategy: validation

Validate before calling

static final Pattern LOWER_HEX = Pattern.compile("^[0-9a-f]{1,32}$");

boolean isValidZipkinId(String s) {
  return s != null && LOWER_HEX.matcher(s).matches();
}

String sanitize(String s) {
  return s == null ? null : s.replace("-", "")
      .replaceFirst("^0x", "").toLowerCase(Locale.ROOT);
}

Prevention

When it happens

Trigger: Calling .id("ABC"), .traceId("0x4bf1"), .parentId("4BF1F00C-E581"), or any setter with an ID containing a non-hex character or uppercase letter.

Common situations: Handing UUID strings (with dashes and uppercase) straight to the builder; receiving IDs from systems that emit uppercase hex (many .NET/Go toolchains default to lowercase but custom ones don't); pasting IDs from logs with formatting artifacts.

Related errors


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