{"record":{"id":"7a29d13dc8ecdfdb","repo":"eclipse-vertx/vert.x","slug":"http-client-must-be-configured-for-at-least-one-ht","errorCode":null,"errorMessage":"HTTP client must be configured for at least one HTTP version","messagePattern":"HTTP client must be configured for at least one HTTP version","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBuilderInternal.java","lineNumber":297,"sourceCode":"\n  @Override\n  public HttpClientInternal build() {\n\n    HttpClientConfig co = clientConfig;\n    if (co == null) {\n      // We assume default client configuration\n      co = new HttpClientConfig(new HttpClientOptions());\n    }\n\n    ClientSSLOptions ssl;\n    if (sslOptions != null) {\n      ssl = sslOptions.copy();\n    } else {\n      ssl = null;\n    }\n\n    if (co.getVersions().isEmpty()) {\n      throw new IllegalStateException(\"HTTP client must be configured for at least one HTTP version\");\n    }\n\n    HttpClientMetrics<?, ?> httpMetrics;\n    if (vertx.metrics() != null) {\n      httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpClientMetrics(co) : null;\n    } else {\n      httpMetrics = null;\n    }\n\n    HttpClientTransport quicTransport;\n    if (co.getVersions().contains(HttpVersion.HTTP_3)) {\n      quicTransport = new QuicHttpClientTransport(vertx, co);\n    } else {\n      quicTransport = null;\n    }\n\n    HttpClientTransport transport;\n    String shared;","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientBuilderInternal.java#L279-L315","documentation":"HttpClientBuilderInternal.build checks that the composed HttpClientOptions contains at least one protocol version. If setProtocolVersion/alpnVersions configuration results in an empty versions list, build throws this IllegalStateException — the client cannot negotiate any HTTP protocol.","triggerScenarios":"Calling build() after configuring options whose version set is empty — e.g. via JSON config {\"alpnVersions\": []} combined with settings that drop default versions, or custom option composition that clears versions.","commonSituations":"Hand-edited JSON client configs; programmatically building options by removing versions for HTTP/1-only testing and removing all of them; merging config overlays that override arrays with empty arrays.","solutions":["Ensure HttpClientOptions has at least one version, e.g. setProtocolVersion(HttpVersion.HTTP_2) or keep HTTP_1_1 default.","Fix the JSON config so alpnVersions/versions arrays are non-empty.","Validate the merged options with co.getVersions().isEmpty() before calling build()."],"exampleFix":"// before\nHttpClientOptions opts = new HttpClientOptions().setAlpnVersions(List.of());\nHttpClient client = vertx.httpClientBuilder().with(options(opts)).build();\n// after\nHttpClientOptions opts = new HttpClientOptions().setAlpnVersions(List.of(HttpVersion.HTTP_1_1, HttpVersion.HTTP_2));\nHttpClient client = vertx.httpClientBuilder().with(options(opts)).build();","handlingStrategy":"validation","validationCode":"if (co.getVersions().isEmpty()) {\n  co.setProtocolVersion(HttpVersion.HTTP_1_1);\n}\nHttpClient client = vertx.httpClientBuilder().with(options(co)).build();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never set alpnVersions to an empty list in JSON configs.","After merging config overlays, assert versions are non-empty before build().","Keep the HTTP_1_1 default unless you deliberately configure alternatives."],"tags":["http-client","configuration","protocol"],"backgroundTag":"missing-required-config","analyzedSha":"fb308bd8c3f12c79f4ae89bef67fadf6c80d036e","analyzedAt":"2026-09-06T11:37:12.241Z","contentChangedAt":"2026-09-06T11:37:12.241Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}