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
- Percent-encode disallowed characters before setting the value (space -> %20, etc.)
- Use a standard encoder per component (URLEncoder for query values, URI path encoding for paths)
- Restrict or normalize input (e.g. trim, ASCII-fold) before building the URI
- 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
- Percent-encode any user-controlled value before placing it in a URI
- Use the right encoder per component (query vs path)
- Strip/normalize illegal characters (spaces, <, >, |, unicode) early
- Keep raw file paths out of URI paths — encode first
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid host or port:
- Has authority -- Non-empty path must start with '/'
- No authority -- Path cannot start with '//'
- Missing required scheme.
- Scheme must start with an alphabetic char
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/2dc31abb07fc0f12.
Report an issue: GitHub.