karatelabs/karate · warning

cannot parse the request URI:

Error message

cannot parse the request URI: 

What it means

The embedded Karate HTTP server failed while Netty was decoding the request URI/query string (e.g. a bad percent-escape like `?state=%zz`). Previously this exception escaped channelRead0 into the pipeline tail, so no response was written and the client hung; the handler now catches it and replies 400 Bad Request with the decoder's message. It is a server-side guard against malformed request lines.

Solutions

  1. Fix the client to percent-encode the URI/query correctly (use a URL builder / encodeURIComponent)
  2. Reproduce the failing URL and inspect the exact decode error in the server's warn log
  3. If the request comes from a third-party redirect flow, encode query params once, not twice
  4. If the request is intentionally malformed and you just need it handled, add a custom handler or treat the 400 as the expected result

Example fix

// before
'/callback?state=%zz' // 400 cannot parse the request URI
// after
'/callback?state=' + encodeURIComponent(state)
Defensive patterns

Strategy: validation

Validate before calling

// client-side pre-check in JS
function safeUrl(raw) {
  try { new URL(raw); return raw; } catch (e) { return encodeURI(raw); }
}
// ensure every query param value is encoded: encodeURIComponent(value)

Try / catch

// server test: assert the mock returns 400 rather than hanging
response = httpGet('/callback?state=%zz');
assert response.status == 400;

Prevention

When it happens

Trigger: Sending an HTTP request to a Karate-embedded (mock/proxy) server whose URI contains invalid percent-encoding, illegal characters in the query string, or otherwise unparsable URI syntax that makes Netty's QueryStringDecoder throw.

Common situations: Mock-server tests driven by misconfigured OAuth/OIDC callbacks where `state` or `redirect_uri` params are double-encoded or contain raw `%`; fuzzing tools; clients building URLs manually without URL-encoding.

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 karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/cea005bdae9a145d. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/http/HttpServerHandler.java:67

    HttpServerHandler(HttpServer server) {
        this.server = server;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
        HttpRequest request;
        try {
            request = toRequest(req);
        } catch (Exception e) {
            // A malformed REQUEST LINE fails here, before any handler exists to answer it — a bad
            // percent-escape in the query string (`?state=%zz`) makes Netty's QueryStringDecoder throw
            // while we are still building the request. This used to escape channelRead0 into the pipeline
            // tail, which logs the exception and writes NOTHING: the client then waits for a response that
            // will never come, while the server happily serves every other connection. A hung request is
            // worse than any wrong status, so say 400 — the client sent something we cannot parse.
            logger.warn("bad request '{}': {}", req.uri(), e.getMessage());
            ctx.writeAndFlush(error(HttpResponseStatus.BAD_REQUEST,
                    "cannot parse the request URI: " + e.getMessage()));
            return;
        }
        if (server.wsHandler != null && isWsUpgrade(req)) {
            try {
                handleWsUpgrade(ctx, req, request);
            } catch (Exception e) {
                String message = e.getMessage();
                logger.error("ws upgrade error: {}", message);
                ctx.writeAndFlush(error(message));
            }
            return;
        }
        if (server.sseHandler != null && isSseRequest(req)) {
            try {
                SseConnection connection = new SseConnection(ctx);
                server.sseHandler.accept(request, connection);
            } catch (Exception e) {

View on GitHub (pinned to a22eb90246)