karatelabs/karate · error · RuntimeException

proceed() needs a target URL or Host header in request

Error message

proceed() needs a target URL or Host header in request

What it means

HttpRequest.proceed(targetUrl) re-sends/replays the request against a new target. If targetUrl is null it falls back to building http://<Host header>. When neither a target URL nor a Host header exists there is nowhere to forward the request, so it throws.

Solutions

  1. Pass an explicit target: request.proceed("https://other-host.example")
  2. Set a Host header before proceeding: request.header("Host", "other-host.example") then proceed(null)
  3. If replaying on the same host, pass the original base URL rather than null
  4. When intercepting/proxying, copy the inbound Host header onto the HttpRequest before calling proceed

Example fix

// before
response.getRequest().proceed(null); // throws when Host header absent
// after
response.getRequest().proceed("http://backend.internal:8080");
Defensive patterns

Strategy: try-catch

Validate before calling

var req = response.getRequest();
if (targetUrl == null && req.getHeader("Host") == null) {
  throw new Error('proceed() requires a target URL; request has no Host header');
}

Try / catch

try {
  response.getRequest().proceed(targetUrl);
} catch (RuntimeException e) {
  if (e.getMessage().contains("proceed() needs a target URL")) {
    // fall back to the original absolute URL
    response.getRequest().proceed(originalBaseUrl);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling request.proceed(null) (or proceed() via a binding that omits the arg) on a request that has no 'Host' header set — e.g. a request built programmatically with only a path, or a request whose Host header was stripped by an intermediary or by the caller before proceeding.

Common situations: Implementing proxy/redirect-following logic where the upstream response/request lost its Host header; testing forward() style flows with hand-built HttpRequest objects; environment changes where requests previously came with Host headers but now come from a builder that never set one.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/c3a29b25ec4888f3. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/http/HttpRequest.java:709

        return args -> {
            if (args.length > 0) {
                return getFiles(args[0] + "");
            } else {
                throw new RuntimeException("missing argument for files()");
            }
        };
    }

    /**
     * Forward this request to a target URL and return the response.
     * Mirrors {@code karate.proceed()} so JS-file mocks can implement proxy behavior.
     * If {@code targetUrl} is null, uses the {@code Host} header on this request.
     */
    public HttpResponse proceed(String targetUrl) {
        if (targetUrl == null) {
            String host = getHeader("Host");
            if (host == null) {
                throw new RuntimeException("proceed() needs a target URL or Host header in request");
            }
            targetUrl = "http://" + host;
        }
        HttpClient client = httpClient != null ? httpClient : new DefaultHttpClientFactory().create();
        HttpRequestBuilder builder = new HttpRequestBuilder(client);
        builder.url(targetUrl);
        builder.path(path);
        builder.method(method);
        if (headers != null) {
            headers.forEach((name, values) -> {
                String lowerName = name.toLowerCase();
                if (!lowerName.equals("content-length") && !lowerName.equals("host")
                        && !lowerName.equals("transfer-encoding")) {
                    builder.header(name, values);
                }
            });
        }
        Object converted = getBodyConverted();

View on GitHub (pinned to a22eb90246)