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
- Normalize before setting: trim, strip any '0x' prefix, remove separators, and lowercase the value.
- Map UUIDs by removing dashes and lowercasing (a 32-char UUID maps directly to a 128-bit trace ID).
- 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
- Lowercase and strip prefixes/dashes before any ID setter.
- Share one regex validator for all ID fields (traceId, id, parentId).
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.