karatelabs/karate · error · RuntimeException

incomplete http request, 'url' not set

Error message

incomplete http request, 'url' not set

What it means

HttpRequestBuilder.buildInternal() validates that a target URL was configured before assembling the request. If url is still null — nothing called url(...) and no default was supplied — the request is incomplete and the builder throws instead of producing a malformed HttpRequest.

Solutions

  1. Set the URL before executing: builder.url("https://host/path")
  2. Validate the config/env value feeding the URL is non-null and non-empty at startup
  3. Guard dynamic URL construction: throw your own descriptive error if baseUrl is blank
  4. If you only wanted the request shape for logging, still set a placeholder URL or use toCurlCommand() only after url() is called

Example fix

// before
HttpRequestBuilder b = new HttpRequestBuilder(client);
b.invoke(); // throws, url never set
// after
HttpRequestBuilder b = new HttpRequestBuilder(client);
b.url("https://api.example.com/v1/users").invoke();
Defensive patterns

Strategy: validation

Validate before calling

if (baseUrl == null || baseUrl.isEmpty()) throw new IllegalArgumentException('request URL is not configured');
builder.url(baseUrl + path);

Prevention

When it happens

Trigger: Calling build()/invoke()/method()/response() on a builder where url() was never called; passing null to url(); clearing/reset flows that wipe the URL before a second build; using toCurlCommand() on a freshly created builder.

Common situations: Programmatic HTTP calls in Java tests where the URL comes from config/env and the property was empty or missing; template substitution producing empty strings handled as null; copy-pasted builder chains missing the url() line.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/http/HttpRequestBuilder.java:450

                        segments.addAll(StringUtils.split(item, '/', true));
                    }
                }
                builder.setPathSegments(segments);
            }
            URI uri = builder.build();
            return uri.toASCIIString();
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    // Destructive: drains multiPart form fields into URL params when method resolves to GET,
    // and consumes multiPart into body bytes otherwise. Callers must set method() before
    // calling build() on a form/multipart request, or the GET default below will silently
    // rewrite the request.
    private void buildInternal() {
        if (url == null) {
            throw new RuntimeException("incomplete http request, 'url' not set");
        }
        if (method == null) {
            if (multiPart != null && multiPart.isMultipart()) {
                method = "POST";
            } else {
                method = "GET";
            }
        }
        method = method.toUpperCase();
        if ("GET".equals(method) && multiPart != null) {
            Map<String, Object> parts = multiPart.getFormFields();
            if (parts != null) {
                parts.forEach((k, v) -> param(k, (String) v));
            }
            multiPart = null;
        }
        if (multiPart != null) {
            if (body == null) { // this is not-null only for a re-try, don't rebuild multi-part

View on GitHub (pinned to a22eb90246)