grpc/grpc-java · error · IllegalArgumentException

Invalid hex digit in

Error message

Invalid hex digit in ${what} at index ${i} of: ${s}

What it means

Thrown during percent-decoding when a '%' escape is followed by characters that are not valid hexadecimal digits, e.g. '%zz' or '%2G'. The library uses Character.digit(c, 16) and rejects any escape whose two trailing chars are not 0-9/a-f/A-F.

Solutions

  1. Fix or remove the malformed escape; every '%' must be followed by two hex digits
  2. Re-encode the value with a standard URL encoder instead of manual escapes
  3. Decode with a lenient decoder first to inspect what the string actually contains
  4. Escape literal '%' as '%25' if it was never meant as an escape

Example fix

// before
builder.setPath("/a%zzb");
// after
builder.setPath(URLEncoder.encode("/a%zzb", StandardCharsets.UTF_8)); // or correct escape to %2F etc.
Defensive patterns

Strategy: validation

Validate before calling

static boolean hasValidHexEscapes(String s) {
  for (int i = 0; i < s.length(); i++) {
    if (s.charAt(i) == '%') {
      if (i + 2 >= s.length()) return false;
      if (Character.digit(s.charAt(i + 1), 16) == -1 || Character.digit(s.charAt(i + 2), 16) == -1) return false;
      i += 2;
    }
  }
  return true;
}

Try / catch

try { builder.setQuery(value); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Malformed percent-escape in: " + value, e); }

Prevention

When it happens

Trigger: Passing a component string containing malformed escapes like '%GG', '%2x', or '%zz' to a Uri builder setter that validates percent-encoding.

Common situations: Hand-crafted or partially URL-encoded strings; corruption from naive string truncation/repair; encoding bugs that uppercase/lowercase-mangle hex or inject non-hex placeholders.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/9f7eebbf06bdf3b3. Report an issue: GitHub.

Appendix: source

Thrown at api/src/main/java/io/grpc/Uri.java:1063

    ByteBuffer outBuf = ByteBuffer.allocate(s.length());
    percentDecode(s, "input", null, outBuf);
    outBuf.flip();
    return outBuf;
  }

  private static void percentDecode(
      CharSequence s, String what, BitSet allowedChars, ByteBuffer outBuf) {
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (c == '%') {
        if (i + 2 >= s.length()) {
          throw new IllegalArgumentException(
              "Invalid percent-encoding at index " + i + " of " + what + ": " + s);
        }
        int h1 = Character.digit(s.charAt(i + 1), 16);
        int h2 = Character.digit(s.charAt(i + 2), 16);
        if (h1 == -1 || h2 == -1) {
          throw new IllegalArgumentException(
              "Invalid hex digit in " + what + " at index " + i + " of: " + s);
        }
        if (outBuf != null) {
          outBuf.put((byte) (h1 << 4 | h2));
        }
        i += 2;
      } else if (allowedChars == null || allowedChars.get(c)) {
        if (outBuf != null) {
          outBuf.put((byte) c);
        }
      } else {
        throw new IllegalArgumentException("Invalid character in " + what + " at index " + i);
      }
    }
  }

  @Nullable
  private static String percentDecodeAssumedUtf8(@Nullable String s) {

View on GitHub (pinned to 64daddc1f3)