OpenFeign/feign · error · IOException
failed to open GraphQL subscription to
Error message
failed to open GraphQL subscription to ${request.url()} What it means
GraphqlSubscriptionClient opens a WebSocket for a GraphQL subscription via java.net.http.WebSocket. If the asynchronous attach/handshake fails (unreachable server, TLS error, rejected upgrade, timeout), the CompletionException is unwrapped and rethrown as an IOException naming the request URL.
Solutions
- Verify the server supports GraphQL subscriptions (WebSocket upgrade) at that URL and the scheme is ws:// or wss://
- Check proxies/load balancers allow the WebSocket Upgrade headers
- Inspect e.getCause() on the IOException for the underlying CompletionException/ConnectException
- Confirm network reachability (connectivity test to host:port) and TLS certificate validity
Example fix
// before
GraphQL client = GraphQL.builder()
.decoder(decoder).target("https://api.example.com/graphql").build();
client.subscriptions().execute(subscriptionRequest);
// after: ensure the URL/endpoint supports ws upgrades and handle the IOException
try {
client.subscriptions().execute(subscriptionRequest);
} catch (IOException e) {
log.error("subscription setup failed: " + e.getCause(), e);
} Defensive patterns
Strategy: retry
Validate before calling
URI uri = URI.create(request.url());
if (!uri.getScheme().matches("wss?"))
throw new IllegalArgumentException("subscription URL must use ws/wss: " + uri); Try / catch
try {
client.subscriptions().execute(req);
} catch (IOException e) {
if (e.getCause() instanceof CompletionException || e.getMessage().contains("failed to open")) {
// retry with backoff or surface a connectivity problem
retryWithBackoff(req);
} else throw e;
} Prevention
- Confirm the endpoint supports WebSocket subscriptions before enabling them
- Use wss:// with valid certificates in production
- Monitor network/proxy configurations that block WebSocket upgrades
- Add health checks against the subscription endpoint
When it happens
Trigger: subscribe()/execute() called and subscription.attach(...).join() completes exceptionally: server does not accept WebSocket upgrade at the URL, network/TLS failure, wrong scheme (http instead of ws), or the join() future times out.
Common situations: GraphQL endpoint does not support subscriptions, a reverse proxy blocks WebSocket upgrades, the URL points at a plain HTTP route, firewall blocks ws/wss traffic, server down.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/fb1e6039e26e0807.
Report an issue: GitHub.
Appendix: source
Thrown at graphql/src/main/java/feign/graphql/GraphqlSubscriptionClient.java:128
var builder = httpClient.newWebSocketBuilder().subprotocols("graphql-transport-ws");
if (options != null && options.connectTimeoutMillis() > 0) {
builder.connectTimeout(Duration.ofMillis(options.connectTimeoutMillis()));
}
request
.headers()
.forEach(
(name, values) -> {
if (isForwardable(name)) {
values.forEach(value -> builder.header(name, value));
}
});
try {
subscription.attach(builder.buildAsync(webSocketUri(request.url()), subscription).join());
} catch (CompletionException e) {
var cause = e.getCause() == null ? e : e.getCause();
throw new IOException("failed to open GraphQL subscription to " + request.url(), cause);
}
// 204 keeps feign's logger from draining and replacing the body, which would drop the live
// subscription. Nothing here ever crosses the wire.
return Response.builder()
.status(HttpURLConnection.HTTP_NO_CONTENT)
.reason("Subscribed")
.request(request)
.headers(Collections.emptyMap())
.body(subscription)
.build();
}
/** Headers the JDK WebSocket handshake rejects or manages itself. */
private static boolean isForwardable(String header) {
var name = header.toLowerCase(Locale.ROOT);
return !name.equals("connection")
&& !name.equals("upgrade")View on GitHub (pinned to e2a1e27560)