{"record":{"id":"9b72c2c7d445fcbe","repo":"openzipkin/zipkin","slug":"should-be-a-1-to-32-character-lower-hex-string-wi","errorCode":null,"errorMessage":" should be a 1 to 32 character lower-hex string with no prefix","messagePattern":" should be a 1 to 32 character lower-hex string with no prefix","errorType":"exception","errorClass":"NumberFormatException","httpStatus":null,"severity":"error","filePath":"zipkin/src/main/java/zipkin2/internal/HexCodec.java","lineNumber":48,"sourceCode":"   */\n  public static long lowerHexToUnsignedLong(String lowerHex, int index) {\n    long result = 0;\n    for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) {\n      char c = lowerHex.charAt(index);\n      result <<= 4;\n      if (c >= '0' && c <= '9') {\n        result |= c - '0';\n      } else if (c >= 'a' && c <= 'f') {\n        result |= c - 'a' + 10;\n      } else {\n        throw isntLowerHexLong(lowerHex);\n      }\n    }\n    return result;\n  }\n\n  static NumberFormatException isntLowerHexLong(String lowerHex) {\n    throw new NumberFormatException(\n        lowerHex + \" should be a 1 to 32 character lower-hex string with no prefix\");\n  }\n\n  HexCodec() {}\n}\n","sourceCodeStart":30,"sourceCodeEnd":54,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin/src/main/java/zipkin2/internal/HexCodec.java#L30-L54","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlong id = HexCodec.lowerHexToUnsignedLong(headerValue);\n\n// after\nString hex = headerValue.replace(\"-\", \"\")\n    .replaceFirst(\"^0x\", \"\")\n    .toLowerCase(Locale.ROOT);\nlong id = hex.matches(\"[0-9a-f]{1,32}\")\n    ? HexCodec.lowerHexToUnsignedLong(hex)\n    : 0L;","handlingStrategy":"validation","validationCode":"static final Pattern LOWER_HEX_1_32 = Pattern.compile(\"^[0-9a-f]{1,32}$\");\n\nboolean parsable(String s) {\n  return s != null && LOWER_HEX_1_32.matcher(s).matches();\n}\n\nlong parseId(String s) {\n  String hex = s == null ? \"\" : s.replace(\"-\", \"\").replaceFirst(\"^0x\", \"\")\n      .toLowerCase(Locale.ROOT);\n  return parsable(hex) ? HexCodec.lowerHexToUnsignedLong(hex) : 0L;\n}","typeGuard":"boolean isLowerHexId(String s) {\n  return s != null && s.matches(\"^[0-9a-f]{1,32}$\");\n}","tryCatchPattern":"try {\n  id = HexCodec.lowerHexToUnsignedLong(hex);\n} catch (NumberFormatException e) {\n  id = 0L; // treat as absent and generate a new one\n}","preventionTips":["Sanitize UUID/external IDs (strip dashes, lowercase) before hex parsing.","Centralize all hex parsing behind one validating helper."],"tags":["zipkin","hex","codec","number-format","validation"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}