perwendel/spark · error · java.lang.IllegalArgumentException
Bad URI % encoding
Error message
Bad URI % encoding
What it means
UrlDecode.path throws IllegalArgumentException('Bad URI % encoding') when a '%' in a URI path is not followed by two valid hex digits. The decoder requires each %XX triple to be well-formed; anything else is a malformed URI.
Solutions
- Percent-encode the literal '%' as '%25' in client-supplied paths.
- Catch IllegalArgumentException in the request pipeline and respond with HTTP 400 Bad Request.
- Normalize/validate incoming URIs at an outer layer (filter/proxy) before decoding.
- Fix client-side escaping by running the path through proper URL encoding.
Example fix
// before
String path = "/files/100%done.txt";
// after
String path = URLEncoder.encode("/files/100%done.txt", StandardCharsets.UTF_8)
.replace("%2F", "/"); // '%' becomes %25 Defensive patterns
Strategy: try-catch
Validate before calling
boolean hasValidEscapes(String path) {
for (int i = 0; i < path.length(); i++)
if (path.charAt(i) == '%') {
if (i + 2 >= path.length()) return false;
if (Character.digit(path.charAt(i+1), 16) < 0 || Character.digit(path.charAt(i+2), 16) < 0) return false;
i += 2;
}
return true;
} Try / catch
try {
String decoded = UrlDecode.path(rawPath);
} catch (IllegalArgumentException e) {
response.status(400);
return "Bad request: malformed URI encoding";
} Prevention
- Always %-encode literal '%' as '%25' in URLs.
- Sanitize incoming paths at a filter/proxy layer before decoding.
- Use standard client-side URL encoding when building links.
- Add a route-level guard that maps IllegalArgumentException to HTTP 400.
When it happens
Trigger: Requesting a path containing a bare '%' or an incomplete escape like '/foo%2' or '/foo%zz'; the character after '%' is a reserved char that the decoder rejects instead of treating as a valid escape.
Common situations: Clients copying un-encoded URLs containing '%' (e.g. SQL LIKE patterns, format strings) into request paths; proxies/gateways double-decoding URIs; broken template interpolation producing '%{' sequences.
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
- WebSocket handler must implement 'WebSocketListener' or be…
- path cannot be null or blank
- httpMethod cannot be null or blank
- cannot be resolved to absolute file path because it does…
- ${s}
AI-assisted analysis of perwendel/spark@1973e402f5 (2026-09-10).
Data as JSON: /api/errors/2a94d1a5cc418f17.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/spark/utils/urldecoding/UrlDecode.java:56
switch (c) {
case '%':
if (builder == null) {
builder = new Utf8StringBuilder(path.length());
builder.append(path, offset, i - offset);
}
if ((i + 2) < end) {
char u = path.charAt(i + 1);
if (u == 'u') {
// TODO this is wrong. This is a codepoint not a char
builder.append((char) (0xffff & TypeUtil.parseInt(path, i + 2, 4, 16)));
i += 5;
} else {
builder.append((byte) (0xff & (TypeUtil.convertHexDigit(u) * 16
+ TypeUtil.convertHexDigit(path.charAt(i + 2)))));
i += 2;
}
} else {
throw new IllegalArgumentException("Bad URI % encoding");
}
break;
case ';':
if (builder == null) {
builder = new Utf8StringBuilder(path.length());
builder.append(path, offset, i - offset);
}
while (++i < end) {
if (path.charAt(i) == '/') {
builder.append('/');
break;
}
}
break;View on GitHub (pinned to 1973e402f5)