grpc/grpc-java · error · IllegalArgumentException

Invalid character in ${what} at index ${i}

Error message

Invalid character in ${what} at index ${i}

What it means

Thrown during percent-decoding when a plain (non-'%') character is not in the allowed-char set for the component being validated. Each URI component (path, query, fragment, etc.) has its own BitSet of permitted characters; anything else is rejected.

Source

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

          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) {
    if (s == null || s.indexOf('%') == -1) {
      return s;
    }

    ByteBuffer utf8Bytes = percentDecode(s);
    try {
      return StandardCharsets.UTF_8
          .newDecoder()
          .onMalformedInput(CodingErrorAction.REPLACE)
          .onUnmappableCharacter(CodingErrorAction.REPLACE)
          .decode(utf8Bytes)
          .toString();

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Percent-encode disallowed characters before setting the value (space -> %20, etc.)
  2. Use a standard encoder per component (URLEncoder for query values, URI path encoding for paths)
  3. Restrict or normalize input (e.g. trim, ASCII-fold) before building the URI
  4. Check which component you are setting and its allowed-char rules

Example fix

// before
builder.setPath("/docs/chapter 1");
// after
builder.setPath("/docs/" + URLEncoder.encode("chapter 1", StandardCharsets.UTF_8));
Defensive patterns

Strategy: validation

Validate before calling

static String encodeComponent(String raw) {
  return URLEncoder.encode(raw, StandardCharsets.UTF_8); // spaces->+, use path encoder for paths
}

Try / catch

try { builder.setPath(userPath); } catch (IllegalArgumentException e) { builder.setPath(URLEncoder.encode(userPath, StandardCharsets.UTF_8)); }

Prevention

When it happens

Trigger: Setting a Uri component with characters illegal for that component, e.g. spaces, '<', '>', '"', '{', '}', '|' or non-ASCII characters in a path/query passed through a validating setter.

Common situations: Raw user input or file paths placed directly into a URI path; Unicode identifiers in hostnames/paths; spaces in query values that were never encoded.

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/2dc31abb07fc0f12. Report an issue: GitHub.