openzipkin/zipkin · error · NumberFormatException
should be a 1 to 32 character lower-hex string with no pref
Error message
should be a 1 to 32 character lower-hex string with no prefix
What it means
HexCodec.lowerHexToHexString / related conversions throw NumberFormatException('<value> should be a 1 to 32 character lower-hex string with no prefix') via isntLowerHexLong when a character falls outside [0-9a-f]. This is the internal hex-parsing path for IDs: uppercase hex, '0x' prefixes, dashes (UUID style), or > 32 chars all fail. NumberFormatException (not IllegalArgumentException) is used because it is the conventional exception for bad numeric parsing.
Source
Thrown at zipkin/src/main/java/zipkin2/internal/HexCodec.java:48
*/
public static long lowerHexToUnsignedLong(String lowerHex, int index) {
long result = 0;
for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) {
char c = lowerHex.charAt(index);
result <<= 4;
if (c >= '0' && c <= '9') {
result |= c - '0';
} else if (c >= 'a' && c <= 'f') {
result |= c - 'a' + 10;
} else {
throw isntLowerHexLong(lowerHex);
}
}
return result;
}
static NumberFormatException isntLowerHexLong(String lowerHex) {
throw new NumberFormatException(
lowerHex + " should be a 1 to 32 character lower-hex string with no prefix");
}
HexCodec() {}
}
View on GitHub (pinned to 878ce2a1fa)
Solutions
- Sanitize before parsing: strip '0x'/'-', lowercase, and validate length 1-32.
- For UUIDs: uuid.toString().replace("-", "") yields a valid 32-char lower-hex string.
- Validate with a regex ^[0-9a-f]{1,32}$ first and reject/log non-conforming inputs instead of relying on the exception.
Example fix
// before
long id = HexCodec.lowerHexToUnsignedLong(headerValue);
// after
String hex = headerValue.replace("-", "")
.replaceFirst("^0x", "")
.toLowerCase(Locale.ROOT);
long id = hex.matches("[0-9a-f]{1,32}")
? HexCodec.lowerHexToUnsignedLong(hex)
: 0L; Defensive patterns
Strategy: validation
Validate before calling
static final Pattern LOWER_HEX_1_32 = Pattern.compile("^[0-9a-f]{1,32}$");
boolean parsable(String s) {
return s != null && LOWER_HEX_1_32.matcher(s).matches();
}
long parseId(String s) {
String hex = s == null ? "" : s.replace("-", "").replaceFirst("^0x", "")
.toLowerCase(Locale.ROOT);
return parsable(hex) ? HexCodec.lowerHexToUnsignedLong(hex) : 0L;
} Type guard
boolean isLowerHexId(String s) {
return s != null && s.matches("^[0-9a-f]{1,32}$");
} Try / catch
try {
id = HexCodec.lowerHexToUnsignedLong(hex);
} catch (NumberFormatException e) {
id = 0L; // treat as absent and generate a new one
} Prevention
- Sanitize UUID/external IDs (strip dashes, lowercase) before hex parsing.
- Centralize all hex parsing behind one validating helper.
When it happens
Trigger: Calling HexCodec.lowerHexToHexString("ABC"), passing a UUID with dashes, a '0x'-prefixed value, or an ID longer than 32 chars; also reached indirectly from code that parses B3 header values with this codec.
Common situations: Bridging IDs from external systems (UUID-based correlation IDs, .NET Guid strings with uppercase hex) into zipkin-compatible IDs; parsing trace context headers that were mangled by proxies or logging frameworks.
Related errors
- should be lower-hex encoded with no prefix
- Expected json, proto3 or thrift list encoding
- input is not a list
- List<Span> implies at least traceId and id fields
- v1 format is not supported. For help, contact https://gitter
AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14).
Data as JSON: /api/errors/9b72c2c7d445fcbe.
Report an issue: GitHub.