Repository: quarkusio/quarkus
Issue: Unpredictable reactive controller behavior. 

### Describe the bug

I decided to refactor one of my service written in quarkus from blocking io to reactive manner.

I have many mock controller which works perfectly before refactor but now some of them throws errors:

```
java.lang.NullPointerException: Cannot invoke "org.jboss.resteasy.reactive.server.mapping.RuntimeResource.getResourceClass()" because the return value of "org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext.getTarget()" is null
	at org.jboss.resteasy.reactive.server.core.ExceptionMapping.logBlockingErrorIfRequired(ExceptionMapping.java:103)
	at org.jboss.resteasy.reactive.server.core.ExceptionMapping.mapException(ExceptionMapping.java:95)
	at org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext.mapExceptionIfPresent(ResteasyReactiveRequestContext.java:340)
	at org.jboss.resteasy.reactive.server.handlers.ExceptionHandler.handle(ExceptionHandler.java:15)
	at org.jboss.resteasy.reactive.server.handlers.ExceptionHandler.handle(ExceptionHandler.java:9)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
	at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:47)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:17)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:7)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1127)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:151)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
	at io.quarkus.vertx.http.runtime.StaticResourcesRecorder.lambda$start$1(StaticResourcesRecorder.java:67)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1127)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:151)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:351)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:329)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1127)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:151)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:55)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$10.handle(VertxHttpRecorder.java:443)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$10.handle(VertxHttpRecorder.java:440)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:151)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:133)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:67)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:30)
	at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50)
	at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:168)
	at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:143)
	at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:155)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:154)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:99)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.vertx.core.http.impl.Http1xUpgradeToH2CHandler.channelRead(Http1xUpgradeToH2CHandler.java:116)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:311)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:432)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.end(Http1xOrH2CHandler.java:61)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.channelRead(Http1xOrH2CHandler.java:38)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:795)
	at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:480)
	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:831)
``` 

For example this controller doesn't work:
```java
package pl.test.rest.recaptcha;

import io.smallrye.mutiny.Uni;
import pl.test.rest.recaptcha.model.SiteVerifyResponse;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Date;

@Path("/recaptcha/api/siteverify")
public class MockResource {
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Uni<SiteVerifyResponse> siteVerify(@FormParam("response") String response
            , @FormParam("secret") String secret, @FormParam("remoteip") String remoteip) {
        return Uni.createFrom().item(SiteVerifyResponse.builder()
                .success(true)
                .score((float) 1.0)
                .action("ACTION")
                .hostname("localhost")
                .challengeTs(new Date())
                .build());
    }
}
```

But this work's:
```java
package pl.test.rest.dotpay;

import io.smallrye.mutiny.Uni;
import lombok.val;
import pl.test.rest.dotpay.model.PaymentEntry;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.Collections;
import java.util.List;

@Path("/s2/login/api/purchase_history/")
public class MockResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Uni<List<PaymentEntry>> api(@QueryParam("email") String email) {
        return Uni.createFrom().item(Collections.singletonList(createPaymentEntry()));
    }

    private PaymentEntry createPaymentEntry() {
        val entry = new PaymentEntry();
        entry.setDate("2018-06-09T16:54:15.855823");
        entry.setAmount("8.29");
        entry.setCurrency("PLN");
        entry.setStatus("completed");
        entry.setRequestIp("188.146.162.128");
        entry.setMcc("4112");
        entry.setChannel("73");
        entry.setMerchantId("5d51e2dc7bc76cf9841d82ba404d134646bdbd97");

        return entry;
    }
}
```
The one thing what I'am done in refactor is change the return type to new one with wrapper Uni.

I found on network similar error:
https://github.com/quarkusio/quarkus/runs/3204982867

### Expected behavior

_No response_

### Actual behavior

_No response_

### How to Reproduce?

_No response_

### Output of `uname -a` or `ver`

Linux 5.13.0-20-generic #20-Ubuntu SMP Fri Oct 15 14:21:35 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

### Output of `java -version`

16.0.2

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.4.0.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.6.2

### Additional information

_No response_

Changed files:
independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java
independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java

diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java
index 1b08a3ec8c7..adb8f41f953 100644
--- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java
+++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java
@@ -161,7 +161,7 @@ public void run() {
                     if (t instanceof PreserveTargetException) {
                         handleException(t.getCause(), true);
                     } else {
-                        handleException(t);
+                        handleException(t, true);
                     }
                     if (aborted) {
                         return;
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java
index f1d0d44ea47..db68bf80bc7 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ExceptionMapping.java
@@ -22,6 +22,7 @@
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.reactive.ResteasyReactiveClientProblem;
 import org.jboss.resteasy.reactive.common.model.ResourceExceptionMapper;
+import org.jboss.resteasy.reactive.server.mapping.RuntimeResource;
 import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveAsyncExceptionMapper;
 import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveExceptionMapper;
 import org.jboss.resteasy.reactive.spi.BeanFactory;
@@ -99,31 +100,46 @@ public void mapException(Throwable throwable, ResteasyReactiveRequestContext con
 
     private void logBlockingErrorIfRequired(Throwable throwable, ResteasyReactiveRequestContext context) {
         if (isBlockingProblem(throwable)) {
-            log.error("A blocking operation occurred on the IO thread. This likely means you need to annotate "
-                    + context.getTarget().getResourceClass().getName() + "#" + context.getTarget().getJavaMethodName() + "("
-                    + Arrays.stream(context.getTarget().getParameterTypes()).map(Objects::toString)
-                            .collect(Collectors.joining(", "))
-                    + ") with @"
-                    + Blocking.class.getName()
-                    + ". Alternatively you can annotate the class " + context.getTarget().getResourceClass().getName()
-                    + " to make every method on the class blocking, or annotate your sub class of the "
-                    + Application.class.getName() + " class to make the whole application blocking");
+            RuntimeResource runtimeResource = context.getTarget();
+            if (runtimeResource == null) {
+                log.error("A blocking operation occurred on the IO thread. This likely means you need to use the @"
+                        + Blocking.class.getName() + " annotation on the Resource method, class or "
+                        + Application.class.getName() + " class.");
+            } else {
+                log.error("A blocking operation occurred on the IO thread. This likely means you need to annotate "
+                        + runtimeResource.getResourceClass().getName() + "#" + runtimeResource.getJavaMethodName() + "("
+                        + Arrays.stream(runtimeResource.getParameterTypes()).map(Objects::toString)
+                                .collect(Collectors.joining(", "))
+                        + ") with @"
+                        + Blocking.class.getName()
+                        + ". Alternatively you can annotate the class " + runtimeResource.getResourceClass().getName()
+                        + " to make every method on the class blocking, or annotate your sub class of the "
+                        + Application.class.getName() + " class to make the whole application blocking");
+            }
         }
     }
 
     private void logNonBlockingErrorIfRequired(Throwable throwable, ResteasyReactiveRequestContext context) {
         if (isNonBlockingProblem(throwable)) {
-            log.error(
-                    "An operation that needed be run on a Vert.x EventLoop thread was run on a worker pool thread. This likely means you need to annotate "
-                            + context.getTarget().getResourceClass().getName() + "#" + context.getTarget().getJavaMethodName()
-                            + "("
-                            + Arrays.stream(context.getTarget().getParameterTypes()).map(Objects::toString)
-                                    .collect(Collectors.joining(", "))
-                            + ") with @"
-                            + NonBlocking.class.getName()
-                            + ". Alternatively you can annotate the class " + context.getTarget().getResourceClass().getName()
-                            + " to make every method on the class run on a Vert.x EventLoop thread, or annotate your sub class of the "
-                            + Application.class.getName() + " class to affect the entire application");
+            RuntimeResource runtimeResource = context.getTarget();
+            if (runtimeResource == null) {
+                log.error(
+                        "An operation that needed be run on a Vert.x EventLoop thread was run on a worker pool thread. This likely means you use the @"
+                                + NonBlocking.class.getName() + " annotation on the Resource method, class or "
+                                + Application.class.getName() + " class.");
+            } else {
+                log.error(
+                        "An operation that needed be run on a Vert.x EventLoop thread was run on a worker pool thread. This likely means you need to annotate "
+                                + runtimeResource.getResourceClass().getName() + "#" + runtimeResource.getJavaMethodName()
+                                + "("
+                                + Arrays.stream(runtimeResource.getParameterTypes()).map(Objects::toString)
+                                        .collect(Collectors.joining(", "))
+                                + ") with @"
+                                + NonBlocking.class.getName()
+                                + ". Alternatively you can annotate the class " + runtimeResource.getResourceClass().getName()
+                                + " to make every method on the class run on a Vert.x EventLoop thread, or annotate your sub class of the "
+                                + Application.class.getName() + " class to affect the entire application");
+            }
         }
     }