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

  1. Percent-encode the literal '%' as '%25' in client-supplied paths.
  2. Catch IllegalArgumentException in the request pipeline and respond with HTTP 400 Bad Request.
  3. Normalize/validate incoming URIs at an outer layer (filter/proxy) before decoding.
  4. 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

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


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)