title: Handling connection errors in Spring reactive webclient
body: <p>I have a spring webclient making http calls to an external service and backed by reactive circuit breaker factory (resilience4J impl). WebClient and circuit breaker behave as expected when the client establishes connection and fails on response (Any internal server or 4XX errors). However, if the client fails to establish connection, either Connection Refused or UnknownHost, it starts to break down.</p>
  <ol>
  <li>I cannot seem to catch the error message  within the webclient and trigger circuit breaker.</li>
  <li>Circuit breaker never opens and throws TimeoutException.<br />
  <code>java.util.concurrent.TimeoutException: Did not observe any item or terminal signal within 1000ms in 'circuitBreaker' (and no fallback has been configured) </code>.</li>
  </ol>
  <p>Error from Web client.<br />
  <code>io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:9000 </code>.</p>
  <p>Here's my code. I have pasted the error origins as well. I have tried to map ConnectException to my custom exception for circuit breaker to pick up but, it did not work.  Can someone help me on handling errors without the response from remote server?</p>
  <pre><code> public Mono&lt;String&gt; toSink(
    Envelope envelope, ConsumerConfiguration webClientConfiguration) {
  
  return getWebClient()
      .post()
      .uri(
          uriBuilder -&gt; {
            if (webClientConfiguration.getPort() != null) {
              uriBuilder.port(webClientConfiguration.getPort());
            }
            return uriBuilder.path(webClientConfiguration.getServiceURL()).build();
          })
      .headers(
          httpHeaders -&gt;
              webClientConfiguration.getHttpHeaders().forEach((k, v) -&gt; httpHeaders.add(k, v)))
      .bodyValue(envelope.toString())
      .retrieve()
      .bodyToMono(Map.class)
      // Convert 5XX internal server error and throw CB exception
      .onErrorResume(
          throwable -&gt; {
            log.error(&quot;Inside the error resume callback of webclient {}&quot;, throwable.toString());
            if (throwable instanceof WebClientResponseException) {
              WebClientResponseException r = (WebClientResponseException) throwable;
              if (r.getStatusCode().is5xxServerError()) {
                return Mono.error(new CircuitBreakerOpenException());
              }
            }
            return Mono.error(new CircuitBreakerOpenException());
          })
      .map(
          map -&gt; {
            log.info(&quot;Response map:{}&quot;, Any.wrap(map).toString());
            return Status.SUCCESS.name();
          })
      .transform(
          it -&gt; {
            ReactiveCircuitBreaker rcb =
                reactiveCircuitBreakerFactory.create(
                    webClientConfiguration.getCircuitBreakerId());
            return rcb.run(
                it,
                throwable -&gt; {
                  /// &quot;Did not observe any item or terminal signal within 1000ms.. &quot; &lt;--- Error here
                  log.info(&quot;throwable in CB {}&quot;, throwable.toString());
                  if (throwable instanceof CygnusBusinessException) {
                    return Mono.error(throwable);
                  }
                  return Mono.error(
                      new CircuitBreakerOpenException(
                          throwable, new CygnusContext(), null, null, null));
                });
          })
      ///io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:9000  &lt;-- Error prints here    
      .onErrorContinue((throwable, o) -&gt; log.error(throwable.toString()))
      .doOnError(throwable -&gt; log.error(&quot;error from webclient:{}&quot;, throwable.toString()));
  </code></pre>
  <p>}</p>