eclipse-vertx/vert.x · error · IllegalArgumentException

Missing :authority / host header

Error message

Missing :authority / host header

What it means

When opening an HTTP/2 CONNECT stream, DefaultHttp2ClientStream requires the request to have an :authority (host) pseudo-header. For CONNECT requests there is no absolute path to derive the authority from, so it must be set explicitly; otherwise the HTTP/2 frame would be malformed per RFC 7540. Vert.x throws IllegalArgumentException before the stream is created.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/http2/DefaultHttp2ClientStream.java:156

    private final boolean end;
    private final Promise<Void> promise;

    public HeadersWrite(HttpRequestHead request, ByteBuf buf, boolean end, Promise<Void> promise) {
      this.request = request;
      this.buf = buf;
      this.end = end;
      this.promise = promise;
    }

    @Override
    public void write() {
      HttpRequestHeaders headers = new HttpRequestHeaders(new DefaultHttp2Headers());
      headers.method(request.method);
      headers.trace(request.traceOperation);
      boolean e;
      if (request.method == HttpMethod.CONNECT) {
        if (request.authority == null) {
          throw new IllegalArgumentException("Missing :authority / host header");
        }
        headers.authority(request.authority);
        // don't end stream for CONNECT
        e = false;
      } else {
        headers.path(request.uri);
        headers.scheme(connection.isSsl() ? "https" : "http");
        if (request.authority != null) {
          headers.authority(request.authority);
        }
        e = end;
      }
      if (request.headers != null && !request.headers.isEmpty()) {
        for (Map.Entry<String, String> header : request.headers) {
          CharSequence headerName = HttpUtils.toLowerCase(header.getKey());
          if (!AsciiString.contentEquals(TRANSFER_ENCODING, headerName)) {
            headers.add(headerName, header.getValue());
          }

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Set the host on RequestOptions: new RequestOptions().setMethod(HttpMethod.CONNECT).setHost("example.com").setPort(443).
  2. Ensure the authority (host[:port]) string is non-null before sending the request.
  3. For generic requests, prefer setServer or a full absolute URI so authority is populated.

Example fix

// before
RequestOptions opts = new RequestOptions().setMethod(HttpMethod.CONNECT).setURI("/tunnel");
// after
RequestOptions opts = new RequestOptions()
  .setMethod(HttpMethod.CONNECT)
  .setHost("upstream.example.com")
  .setPort(443);
Defensive patterns

Strategy: validation

Validate before calling

if (options.getHost() == null || options.getHost().isEmpty()) throw new IllegalArgumentException("CONNECT requires host");

Try / catch

try { client.request(opts); } catch (IllegalArgumentException e) { if (e.getMessage().contains(":authority")) { opts.setHost(defaultAuthority); client.request(opts); } }

Prevention

When it happens

Trigger: Calling HttpClient.request(HttpMethod.CONNECT, ...) or building an HTTP/2 CONNECT request without setting the host/authority on the RequestOptions.

Common situations: Writing a tunnel/proxy client (CONNECT to an upstream host) where the developer passes only a port or omits setHost(); porting HTTP/1.1 CONNECT code that relied on the Host header being synthesized.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/b275721c71d773a1. Report an issue: GitHub.