OpenFeign/feign · error · IOException
Invalid uri
Error message
Invalid uri ${request.url()} What it means
The synchronous java11 Http2Client builds a java.net.http.HttpRequest from the Feign Request's URL. If that URL cannot be parsed as a valid URI (URISyntaxException), it is rethrown as an IOException with the message 'Invalid uri <url>'. Feign templates can produce invalid URLs when unencoded special characters or spaces leak into the path or query.
Solutions
- Log request.url() from the wrapped IOException to see the offending URL
- Encode path/query values: use Util.encode or URLEncoder for user-supplied parameters
- Fix the @RequestLine / base URL (scheme://host syntax, no spaces)
- Percent-encode the target URL before building the client, or use a RequestInterceptor to sanitize URLs
- Verify no template placeholder ({param}) remains unexpanded
Example fix
// before
@RequestLine("GET /search?q={q}") Response search(@Param("q") String q);
api.search("hello world|foo");
// after
api.search(URLEncoder.encode("hello world|foo", StandardCharsets.UTF_8));
// or fix the request line: @RequestLine("GET /search?q={q}") with properly encoded params Defensive patterns
Strategy: validation
Validate before calling
String url = request.url();
try {
new java.net.URI(url); // throws URISyntaxException if invalid
} catch (URISyntaxException e) {
throw new IllegalStateException("Request URL is not a valid URI: " + url, e);
} Type guard
static boolean isValidUri(String s) {
try { new java.net.URI(s); return true; } catch (URISyntaxException e) { return false; }
} Try / catch
try {
return client.execute(request, options);
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Invalid uri")) {
throw new IllegalArgumentException("Fix request URL encoding: " + e.getMessage(), e);
}
throw e;
} Prevention
- Percent-encode all user-supplied path and query values (URLEncoder / Util.encode)
- Validate the Target base URL has scheme://host and no stray whitespace
- Never leave {placeholder} tokens unexpanded in @RequestLine templates
- Add a RequestInterceptor that asserts new URI(url) parses before sending
- Test interfaces with inputs containing spaces, '|', unicode, and CJK characters
When it happens
Trigger: execute() on Http2Client when Request.url() contains characters illegal in a URI - spaces, unencoded '|', '{', '}', non-ASCII characters, or a malformed host/scheme produced by a bad @RequestLine or unexpanded template values.
Common situations: Path/query parameters containing spaces or unicode without Encoding; building URLs manually with Target.EmptyTarget and bad base URLs; Feign version upgrades changing default encoding behavior; template placeholders left unsubstituted.
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
- Status Code [ ] has already been declared to throw [ ] and…
- Cannot generate exception - check constructor parameter…
- Too many constructors marked with @FeignExceptionConstructor
- Cannot find any suitable constructor in class
- Cannot access constructor
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/1b85bdd2bd31dbee.
Report an issue: GitHub.
Appendix: source
Thrown at java11/src/main/java/feign/http2client/Http2Client.java:100
.connectTimeout(Duration.ofMillis(10000))
.build());
}
public Http2Client(Options options) {
this(newClientBuilder(options).version(Version.HTTP_2).build());
}
public Http2Client(HttpClient client) {
this.client = Util.checkNotNull(client, "HttpClient must not be null");
}
@Override
public Response execute(Request request, Options options) throws IOException {
final HttpRequest httpRequest;
try {
httpRequest = newRequestBuilder(request, options).version(client.version()).build();
} catch (URISyntaxException e) {
throw new IOException("Invalid uri " + request.url(), e);
}
HttpClient clientForRequest = getOrCreateClient(options);
HttpResponse<InputStream> httpResponse;
try {
httpResponse = clientForRequest.send(httpRequest, BodyHandlers.ofInputStream());
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException(e);
}
return toFeignResponse(request, httpResponse);
}
@Override
public CompletableFuture<Response> execute(
Request request, Options options, Optional<Object> requestContext) {
HttpRequest httpRequest;View on GitHub (pinned to e2a1e27560)