CDI context propagation issues

20 views
Skip to first unread message

William Burke

unread,
Mar 27, 2026, 9:01:13 AM (7 days ago) Mar 27
to Quarkus Development mailing list
Ok, I'm quite confused about CDI context propagation and I need help.

This is what I got:

* I'm not using smallrye context propagation
* I activate a session context using Arc.container().sessionContext()
* I have a custom scope which I activate
* The custom scope uses a io.quarkus.CurrentContext to hold activated context state
* I'm running within Vertx (event bus)

This is what doesn't work:
* I have a bean that spawns off a worker thread using an injected ManagedExecutor
* Within the worker thread, the request context and my custom scope are propagated correctly and work
* The session context does *NOT* work
* I don't understand why the request and custom scopes work within the worker thread, but the session scope does not.  Looking at io.quarkus.arc.impl.SessionContext, it is almost the same exact code as SessioContext
* I think my custom scope works (and request context too?) because the impl of CurrentContext stores my context state within the Vertx context.
* WTF does Session context NOT work?? I just don't get it...

Here's code.  It fails within emit when the sessionCounter.getCounter() method is called. "inside multi session counter".

        @Inject
        ManagedExecutor managedExecutor;

        @Inject
        ScopedCounterBean counter;

        @Inject
        SessionCounterBean sessionCounter;

        @ChatRoute("multi-executor")
        public Multi<String> multiExecutor() {
            System.out.println("outside multi user message: " + context.request().userMessage());
            counter.increment();
            System.out.println("outside multi chat scoped counter: " + counter.getCounter());
// This session bean code works fine!
            sessionCounter.increment();
            System.out.println("outside multi session counter: " + sessionCounter.getCounter());
            return Multi.createFrom().emitter((emitter) -> {
                managedExecutor.execute(() -> {
                    emit(emitter);
                });
            });
        }

        private void emit(MultiEmitter<? super String> emitter) {
            System.out.println("inside multi user message: " + context.request().userMessage());
            System.out.println("inside multi chat scoped counter: " + counter.getCounter());
// THIS session bean code FAILS!!!
            System.out.println("inside multi session counter: " + sessionCounter.getCounter());
            try {
                for (int i = 0; i < 5; i++) {
                    emitter.emit(Integer.toString(i));
                    Thread.sleep(1);
                }
                emitter.complete();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

--
Bill Burke
IBM

Stephane Epardaud

unread,
Mar 27, 2026, 9:22:33 AM (7 days ago) Mar 27
to quark...@googlegroups.com
Unless I'm mistaken, the request context is only stored and propagated via the Vert.x context by APIs that propagate the Vert.x context.
I don't think ManagedExecutor propagates the Vert.x context. Here, it uses MP-CP to propagate the CDI request context, via its ThreadContextProvider.
If you don't have one for the session context, it won't be propagated.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/quarkus-dev/CAL%3DE%3DjSLV6DQ5mfjgiERLsnjA1fgbN38X5JA-KDzHSxEn3%3DQ7w%40mail.gmail.com.


--
Stéphane Épardaud

William Burke

unread,
Mar 27, 2026, 9:22:38 AM (7 days ago) Mar 27
to Quarkus Development mailing list
Maybe context propagation is on by default?  I do see the ArcContextProvider class that is only propagating the request context.  

So, if that's true I understand why the session context is not propagating, but I DO NOT understand why my custom scope is propagating because I didn't implement a ThreadContextProvider for it.
--
Bill Burke
IBM

William Burke

unread,
Mar 27, 2026, 9:24:33 AM (7 days ago) Mar 27
to quark...@googlegroups.com
On Fri, Mar 27, 2026 at 9:22 AM Stephane Epardaud <stephane...@gmail.com> wrote:
Unless I'm mistaken, the request context is only stored and propagated via the Vert.x context by APIs that propagate the Vert.x context.
I don't think ManagedExecutor propagates the Vert.x context. Here, it uses MP-CP to propagate the CDI request context, via its ThreadContextProvider.
If you don't have one for the session context, it won't be propagated.

Why does my custom scope work then?  I don't have a ThreadContextProvider implemented for it. 

Stephane Epardaud

unread,
Mar 27, 2026, 9:35:01 AM (7 days ago) Mar 27
to quark...@googlegroups.com
On Fri, 27 Mar 2026 at 14:24, 'William Burke' via Quarkus Development mailing list <quark...@googlegroups.com> wrote:
Why does my custom scope work then?  I don't have a ThreadContextProvider implemented for it. 

No idea about that one, I've never used custom scopes.

Ladislav Thon

unread,
Mar 27, 2026, 10:08:51 AM (7 days ago) Mar 27
to quark...@googlegroups.com
Hi!

I don't fully understand what you have (and I haven't seen your code), but I do have a few small pointers.

pá 27. 3. 2026 v 14:01 odesílatel 'William Burke' via Quarkus Development mailing list <quark...@googlegroups.com> napsal:
Ok, I'm quite confused about CDI context propagation and I need help.

This is what I got:

* I'm not using smallrye context propagation

Good, avoid it like a plague! :-D But you use `ManagedExecutor`, which comes from MP Context Propagation. I don't think it makes a huge difference, at least in default configuration. You can just inject `ExecutorService` and hopefully it will all work the same.
 
* I activate a session context using Arc.container().sessionContext()
* I have a custom scope which I activate
* The custom scope uses a io.quarkus.CurrentContext to hold activated context state
* I'm running within Vertx (event bus)

Note that sending a message to the Vert.x event bus is escaping the request boundary. My current thinking is that if you escape the request boundary, you should expect contexts to NOT be propagated (except tracing).
 
This is what doesn't work:
* I have a bean that spawns off a worker thread using an injected ManagedExecutor
* Within the worker thread, the request context and my custom scope are propagated correctly and work
* The session context does *NOT* work

This seems weird, because the ArC's built-in session context uses the very same storage mechanism as the request context (Vert.x duplicated `Context`).

You mention above that you activate the session context manually -- is that in a WebSockets Next request handler? That should have the session context already active. If you're doing things in the WebSockets Next request boundary, including offloading a task to a thread pool I think, the session context should be propagated, just because the Vert.x `Context` is propagated. (But then, you also mentioned you use the Vert.x event bus, which is an entirely different matter.)

Do you have the code somewhere so I can take a look? I know there's probably a ton of other things I don't understand about the code and that are distracting, but it feels like the best way to diagnose the issue, instead of exchanging emails with code snippets.

LT
 
--

William Burke

unread,
Mar 27, 2026, 10:14:31 AM (7 days ago) Mar 27
to quark...@googlegroups.com

William Burke

unread,
Mar 27, 2026, 10:27:52 AM (7 days ago) Mar 27
to quark...@googlegroups.com, Ladislav Thon


On Fri, Mar 27, 2026 at 10:14 AM William Burke <bbu...@redhat.com> wrote:

I did a mvn dependency:tree and if you pull in quarkus-vertx, it pulls in quarkus-smallrye-context-propagation as a transitive dependency.

So @Ladislav Thon if you saw the other email I wrote, the session context is not being propagated for websocket-next for the reasons above.  a ThreadContextProvider needs to be written for it if you want it propagated.  I'll test this out to make sure and let you know.

--
Bill Burke
IBM

Ladislav Thon

unread,
Mar 27, 2026, 10:39:24 AM (7 days ago) Mar 27
to quark...@googlegroups.com
pá 27. 3. 2026 v 15:27 odesílatel 'William Burke' via Quarkus Development mailing list <quark...@googlegroups.com> napsal:
On Fri, Mar 27, 2026 at 10:14 AM William Burke <bbu...@redhat.com> wrote:

I believe ArC's contexts are propagated because the Vert.x duplicated `Context` is propagated; the `ArcContextProvider` plays no role in it. See https://github.com/quarkusio/quarkus/blob/main/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java#L762 -- in fact, it is disabled by default.
 

I did a mvn dependency:tree and if you pull in quarkus-vertx, it pulls in quarkus-smallrye-context-propagation as a transitive dependency.

So @Ladislav Thon if you saw the other email I wrote, the session context is not being propagated for websocket-next for the reasons above.  a ThreadContextProvider needs to be written for it if you want it propagated.  I'll test this out to make sure and let you know.

If you had to implement the `ThreadContextProvider` for it to work, there's a bug somewhere. We better figure it out and fix it.

I'm glad you made it work, but if you have the code somewhere out there, I'd still like to take a look and maybe figure out what's the problem ^^.

LT

Stephane Epardaud

unread,
Mar 27, 2026, 10:59:57 AM (7 days ago) Mar 27
to quark...@googlegroups.com
On Fri, 27 Mar 2026 at 15:39, Ladislav Thon <lad...@gmail.com> wrote:

I believe ArC's contexts are propagated because the Vert.x duplicated `Context` is propagated; the `ArcContextProvider` plays no role in it. See https://github.com/quarkusio/quarkus/blob/main/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java#L762 -- in fact, it is disabled by default.

This was there for Techempower because Sanne wanted a boost.

I think only Matej can say for certain, but my opinion is that for Vert.x threads, the CDI request context is indeed stored in the vertx context and we're not propagating/restoring it.
For thread pools, like ManagedExecutor I think we do propagate the CDI request context using MP-CP because the underlying thread does not have a Vert.x request context.
Also, I don't think Vert.x propagates its context from the event loop requests to the worker threads either. They would get separate contexts with separate values.
--
Stéphane Épardaud

William Burke

unread,
Mar 27, 2026, 11:16:40 AM (7 days ago) Mar 27
to quark...@googlegroups.com
As I said earlier, if you pull in quarkus-vertx, quarkus-vertx pulls in quarkus-smallrye-context-propagation.  ArcConfig.contextPropagation().enabled() is true by default.

FYI, the event bus abstraction (i.e. @ConsumeEvent) and websocket-next both duplicate and runOnContext() for their worker thread interactions.

Martin Kouba

unread,
Mar 27, 2026, 11:18:55 AM (7 days ago) Mar 27
to quark...@googlegroups.com, Stephane Epardaud
On 3/27/26 15:59, Stephane Epardaud wrote:
>
>
> On Fri, 27 Mar 2026 at 15:39, Ladislav Thon <lad...@gmail.com
> <mailto:lad...@gmail.com>> wrote:
>
>
> I believe ArC's contexts are propagated because the Vert.x
> duplicated `Context` is propagated; the `ArcContextProvider` plays
> no role in it. See https://github.com/quarkusio/quarkus/blob/main/
> extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/
> ArcProcessor.java#L762 <https://github.com/quarkusio/quarkus/blob/
> main/extensions/arc/deployment/src/main/java/io/quarkus/arc/
> deployment/ArcProcessor.java#L762> -- in fact, it is disabled by
> default.
>
>
> I don't think that's true: https://github.com/quarkusio/quarkus/blob/
> main/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/
> ArcContextPropagationConfig.java#L19 <https://github.com/quarkusio/
> quarkus/blob/main/extensions/arc/deployment/src/main/java/io/quarkus/
> arc/deployment/ArcContextPropagationConfig.java#L19>
> This was there for Techempower because Sanne wanted a boost.

So, I originally created this config property because of performance
indeed. Because in majority of cases (where Vert.x context is used), the
ArcContextProvider is just a NOOP (see
https://github.com/quarkusio/quarkus/blob/main/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxCurrentContextFactory.java).
When the Vert.x context is not available, there is a threadlocal fallback.

>
> I think only Matej can say for certain, but my opinion is that for
> Vert.x threads, the CDI request context is indeed stored in the vertx
> context and we're not propagating/restoring it.
> For thread pools, like ManagedExecutor I think we do propagate the CDI
> request context using MP-CP because the underlying thread does not have
> a Vert.x request context.

> Also, I don't think Vert.x propagates its context from the event loop
> requests to the worker threads either. They would get separate contexts
> with separate values.



> --
> Stéphane Épardaud
>
> --
> You received this message because you are subscribed to the Google
> Groups "Quarkus Development mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to quarkus-dev...@googlegroups.com <mailto:quarkus-
> dev+uns...@googlegroups.com>.
> To view this discussion visit https://groups.google.com/d/msgid/quarkus-
> dev/CAKU9E9vhqt-dqm9X_cJ6qnQPEooXVYF7EhCuBYVn53nhyaAXdw%40mail.gmail.com
> <https://groups.google.com/d/msgid/quarkus-dev/CAKU9E9vhqt-
> dqm9X_cJ6qnQPEooXVYF7EhCuBYVn53nhyaAXdw%40mail.gmail.com?
> utm_medium=email&utm_source=footer>.

Ladislav Thon

unread,
Mar 27, 2026, 11:47:38 AM (7 days ago) Mar 27
to quark...@googlegroups.com
pá 27. 3. 2026 v 15:59 odesílatel Stephane Epardaud <stephane...@gmail.com> napsal:
On Fri, 27 Mar 2026 at 15:39, Ladislav Thon <lad...@gmail.com> wrote:

I believe ArC's contexts are propagated because the Vert.x duplicated `Context` is propagated; the `ArcContextProvider` plays no role in it. See https://github.com/quarkusio/quarkus/blob/main/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java#L762 -- in fact, it is disabled by default.

This was there for Techempower because Sanne wanted a boost.

OMG it is indeed enabled by default :-D My bad. But as Martin says, it should be a noop in most cases.
 
I think only Matej can say for certain, but my opinion is that for Vert.x threads, the CDI request context is indeed stored in the vertx context and we're not propagating/restoring it.
For thread pools, like ManagedExecutor I think we do propagate the CDI request context using MP-CP because the underlying thread does not have a Vert.x request context.
Also, I don't think Vert.x propagates its context from the event loop requests to the worker threads either. They would get separate contexts with separate values.

I've looked into exactly this a few weeks ago and Vert.x does propagate the duplicated `Context` from an event loop to a worker thread and back. It is still entirely possible to submit a task to a Quarkus worker pool that doesn't have a Vert.x `Context` associated, in which case MP CP would get involved I think.
 
--
Stéphane Épardaud

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

William Burke

unread,
Mar 27, 2026, 6:48:46 PM (6 days ago) Mar 27
to quark...@googlegroups.com
On Fri, Mar 27, 2026 at 11:47 AM Ladislav Thon <lad...@gmail.com> wrote:
pá 27. 3. 2026 v 15:59 odesílatel Stephane Epardaud <stephane...@gmail.com> napsal:
On Fri, 27 Mar 2026 at 15:39, Ladislav Thon <lad...@gmail.com> wrote:

I believe ArC's contexts are propagated because the Vert.x duplicated `Context` is propagated; the `ArcContextProvider` plays no role in it. See https://github.com/quarkusio/quarkus/blob/main/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java#L762 -- in fact, it is disabled by default.

This was there for Techempower because Sanne wanted a boost.

OMG it is indeed enabled by default :-D My bad. But as Martin says, it should be a noop in most cases.

So, I implemented and registered a ThreadContextProvider for the session context and my test example now works and the session context is propagated.

Can you explain what you mean by "it should be a noop in most cases"?

 
 
I think only Matej can say for certain, but my opinion is that for Vert.x threads, the CDI request context is indeed stored in the vertx context and we're not propagating/restoring it.
For thread pools, like ManagedExecutor I think we do propagate the CDI request context using MP-CP because the underlying thread does not have a Vert.x request context.
Also, I don't think Vert.x propagates its context from the event loop requests to the worker threads either. They would get separate contexts with separate values.

I've looked into exactly this a few weeks ago and Vert.x does propagate the duplicated `Context` from an event loop to a worker thread and back. It is still entirely possible to submit a task to a Quarkus worker pool that doesn't have a Vert.x `Context` associated, in which case MP CP would get involved I think.

Without MC-CP there would be no propagation of the request context.  See code links I sent in a earlier email response of why.  I think you can register ignored Vertx context local data keys. If I find out how to do that, I bet if I ignore my custom scope key, that will also break things without a thread context provider.


Ladislav Thon

unread,
Mar 28, 2026, 6:03:04 AM (6 days ago) Mar 28
to Quarkus Development mailing list
Dne pá 27. 3. 2026 23:48 uživatel 'William Burke' via Quarkus Development mailing list <quark...@googlegroups.com> napsal:


On Fri, Mar 27, 2026 at 11:47 AM Ladislav Thon <lad...@gmail.com> wrote:
pá 27. 3. 2026 v 15:59 odesílatel Stephane Epardaud <stephane...@gmail.com> napsal:
On Fri, 27 Mar 2026 at 15:39, Ladislav Thon <lad...@gmail.com> wrote:

I believe ArC's contexts are propagated because the Vert.x duplicated `Context` is propagated; the `ArcContextProvider` plays no role in it. See https://github.com/quarkusio/quarkus/blob/main/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java#L762 -- in fact, it is disabled by default.

This was there for Techempower because Sanne wanted a boost.

OMG it is indeed enabled by default :-D My bad. But as Martin says, it should be a noop in most cases.

So, I implemented and registered a ThreadContextProvider for the session context and my test example now works and the session context is propagated.

Can you explain what you mean by "it should be a noop in most cases"?

OK, you're right and I was wrong. It's been a looong time since I last saw `ArcContexrProvider`, so when I was writing "it should be a noop", I was thinking it accesses the `ThreadLocal` directly. It doesn't. So it does some work, but that work should not matter as long as you're always on threads that have a Vert.x `Context` associated.

I don't know, maybe the `ManagedExecutor` interferes in that it doesn't propagate the Vert.x `Context`? I know that in a bare Vert.x application, the `Context` is propagated from the event loop thread to the worker thread and back, but I didn't try the various executors that exist in Quarkus yet. 

In general, Quarkus currently relies on _two_ APIs to ensure contexts are propagated: MP CP and Vert.x duplicated `Context`. Some contexts use one, some use the other, ArC apparently uses both 🤯 (This is Spartaaaaa!) I'm not sure what to think of my attempt to solve the problem by introducing a third API...

LT


 
 
I think only Matej can say for certain, but my opinion is that for Vert.x threads, the CDI request context is indeed stored in the vertx context and we're not propagating/restoring it.
For thread pools, like ManagedExecutor I think we do propagate the CDI request context using MP-CP because the underlying thread does not have a Vert.x request context.
Also, I don't think Vert.x propagates its context from the event loop requests to the worker threads either. They would get separate contexts with separate values.

I've looked into exactly this a few weeks ago and Vert.x does propagate the duplicated `Context` from an event loop to a worker thread and back. It is still entirely possible to submit a task to a Quarkus worker pool that doesn't have a Vert.x `Context` associated, in which case MP CP would get involved I think.

Without MC-CP there would be no propagation of the request context.  See code links I sent in a earlier email response of why.  I think you can register ignored Vertx context local data keys. If I find out how to do that, I bet if I ignore my custom scope key, that will also break things without a thread context provider.


--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

William Burke

unread,
Mar 28, 2026, 7:40:17 AM (6 days ago) Mar 28
to quark...@googlegroups.com
On Sat, Mar 28, 2026 at 6:03 AM Ladislav Thon <lad...@gmail.com> wrote:
Dne pá 27. 3. 2026 23:48 uživatel 'William Burke' via Quarkus Development mailing list <quark...@googlegroups.com> napsal:


On Fri, Mar 27, 2026 at 11:47 AM Ladislav Thon <lad...@gmail.com> wrote:
pá 27. 3. 2026 v 15:59 odesílatel Stephane Epardaud <stephane...@gmail.com> napsal:
On Fri, 27 Mar 2026 at 15:39, Ladislav Thon <lad...@gmail.com> wrote:

I believe ArC's contexts are propagated because the Vert.x duplicated `Context` is propagated; the `ArcContextProvider` plays no role in it. See https://github.com/quarkusio/quarkus/blob/main/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java#L762 -- in fact, it is disabled by default.

This was there for Techempower because Sanne wanted a boost.

OMG it is indeed enabled by default :-D My bad. But as Martin says, it should be a noop in most cases.

So, I implemented and registered a ThreadContextProvider for the session context and my test example now works and the session context is propagated.

Can you explain what you mean by "it should be a noop in most cases"?

OK, you're right and I was wrong. It's been a looong time since I last saw `ArcContexrProvider`, so when I was writing "it should be a noop", I was thinking it accesses the `ThreadLocal` directly. It doesn't. So it does some work, but that work should not matter as long as you're always on threads that have a Vert.x `Context` associated.

I don't know, maybe the `ManagedExecutor` interferes in that it doesn't propagate the Vert.x `Context`? I know that in a bare Vert.x application, the `Context` is propagated from the event loop thread to the worker thread and back, but I didn't try the various executors that exist in Quarkus yet. 


ManagedExecutor doesn't interfere with anything.  It is the vertx extension itself that interferes.  Duplicating the context does not copy Arc keys to local data:
I think the reason for this is that MP-CP allows app dev to choose what contexts get propagated.  I tested this out by adding my custom scope key to the ignore list, and this broke my custom scope propagation.

 
In general, Quarkus currently relies on _two_ APIs to ensure contexts are propagated: MP CP and Vert.x duplicated `Context`. Some contexts use one, some use the other, ArC apparently uses both 🤯 (This is Spartaaaaa!) I'm not sure what to think of my attempt to solve the problem by introducing a third API...


Arc *only* uses MC-CP and only for request context.  (See above).  I'm actually not sure why why there's a config switch [3] to turn off this default behavior. Maybe an artifact of backward compatibility? 


Thanks for the conversation guys.  I know I answered my own questions, but this thread helped me figure it out. 

Bill

Ladislav Thon

unread,
Mar 28, 2026, 10:27:09 AM (6 days ago) Mar 28
to quark...@googlegroups.com
OK, I think I've got it (couldn't resist). There's a big difference between `Vertx.executeBlocking()` and `ExecutorService.submit()` and `ManagedExecutor.submit()`, as one can observe using this simple program:
@RequestScoped
public class MyRequest {
private String value = "";

public String get() {
return value;
}

public void set(String value) {
this.value = value;
}
}
@Path("/")
public class MyResource {
@Inject
MyRequest request;

@Inject
io.vertx.core.Vertx vertx;

@Inject
io.vertx.mutiny.core.Vertx mutinyVertx;

@Inject
java.util.concurrent.ExecutorService executor;

@Inject
org.eclipse.microprofile.context.ManagedExecutor managedExecutor;

@GET
@Path("/vertx")
public Uni<String> vertx() {
request.set("foobar");
return Uni.createFrom().emitter(em -> {
vertx.executeBlocking(() -> {
Log.info("Vert.x: " + request.get());
em.complete("Vert.x");
return null;
});
});
}

@GET
@Path("/mutiny-vertx")
public Uni<String> mutinyVertx() {
request.set("foobar");
return mutinyVertx.executeBlocking(() -> {
Log.info("Mutiny Vert.x: " + request.get());
return "Mutiny Vert.x";
});
}

@GET
@Path("/executor")
public Uni<String> executor() {
request.set("foobar");
return Uni.createFrom().emitter(em -> {
executor.submit(() -> {
try {
Log.info("Executor: " + request.get());
} catch (Exception e) {
Log.error(e);
}
em.complete("Executor");
});
});
}

@GET
@Path("/managed-executor")
public Uni<String> managedExecutor() {
request.set("foobar");
return Uni.createFrom().emitter(em -> {
managedExecutor.submit(() -> {
try {
Log.info("Managed Executor: " + request.get());
} catch (Exception e) {
Log.error(e);
}
em.complete("Managed Executor");
});
});
}
}
Regardless of the value of `quarkus.arc.context-propagation.enabled`, the first 2 methods always propagate the request context, because Vert.x propagates its `Context`.

The 3rd method never propagates the request context, because it doesn't propagate the Vert.x `Context` and it doesn't use MP CP.

The 4th method only propagates the request context if `quarkus.arc.context-propagation.enabled` is `true`, because it doesn't propagate the Vert.x `Context`, but it does use MP CP.

I guess this makes sense, but I was so focused on `Vertx.executeBlocking()` recently that I forgot to take the other APIs into account, thinking they work the same. Clearly they don't. On the other hand, this is quite a mess...

I also tried with WebSockets Next, where `MyRequest` is the same as above and `MySession` is exactly the same as `MyRequest`, except it's `@SessionScoped`:
@WebSocket(path = "/ws")
public class MyEndpoint {
@Inject
MyRequest request;

@Inject
MySession session;

@Inject
io.vertx.core.Vertx vertx;

@Inject
io.vertx.mutiny.core.Vertx mutinyVertx;

@Inject
java.util.concurrent.ExecutorService executor;

@Inject
org.eclipse.microprofile.context.ManagedExecutor managedExecutor;

@OnTextMessage
public Uni<String> vertx(String message) {
request.set("foobar");
session.set("quuuux");
return Uni.createFrom().emitter(em -> {
vertx.executeBlocking(() -> {
Log.info("Vert.x: " + request.get());
Log.info("Vert.x: " + session.get());
em.complete("Vert.x");
return null;
});
});
}

public Uni<String> mutinyVertx(String message) {
request.set("foobar");
session.set("quuuux");
return mutinyVertx.executeBlocking(() -> {
Log.info("Mutiny Vert.x: " + request.get());
Log.info("Mutiny Vert.x: " + session.get());
return "Mutiny Vert.x";
});
}

public Uni<String> executor(String message) {
request.set("foobar");
session.set("quuuux");
return Uni.createFrom().emitter(em -> {
executor.submit(() -> {
try {
Log.info("Executor: " + request.get());
} catch (Exception e) {
Log.error(e);
}
try {
Log.info("Executor: " + session.get());
} catch (Exception e) {
Log.error(e);
}
em.complete("Executor");
});
});
}

public Uni<String> managedExecutor(String message) {
request.set("foobar");
session.set("quuuux");
return Uni.createFrom().emitter(em -> {
managedExecutor.submit(() -> {
try {
Log.info("Managed Executor: " + request.get());
} catch (Exception e) {
Log.error(e);
}
try {
Log.info("Managed Executor: " + session.get());
} catch (Exception e) {
Log.error(e);
}
em.complete("Managed Executor");
});
});
}
}
To check, one has to move the `@OnTextMessage` annotation between runs, so it's less pleasant, but at the moment, I think I expected the results.

Regardless of the value of `quarkus.arc.context-propagation.enabled`, the first 2 methods always propagate the request context and the session context, because Vert.x propagates its `Context`.

The 3rd method never propagates neither the request context nor the session context, because it doesn't propagate the Vert.x `Context` and it doesn't use MP CP.

The 4th method only propagates the request context if `quarkus.arc.context-propagation.enabled` is `true` and it never propagates the session context, because:

- it doesn't propagate the Vert.x `Context`
- it does use MP CP
- the `ArcContextProvider` only propagates the request context, not the session context (this honestly feels like a bug)

I think I learnt more about our current situation, as sad as it is. Hopefully I'm not the only one :-)

Will follow up next week.

LT

so 28. 3. 2026 v 12:40 odesílatel 'William Burke' via Quarkus Development mailing list <quark...@googlegroups.com> napsal:
--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Ladislav Thon

unread,
Mar 30, 2026, 3:00:32 AM (4 days ago) Mar 30
to quark...@googlegroups.com
I just modified the example code to also use the Vert.x duplicated `Context` directly, like so:
@GET
@Path("/vertx")
public Uni<String> vertx() {
request.set("foobar");
    Vertx.currentContext().putLocal("my-id", "bazqux");

return Uni.createFrom().emitter(em -> {
vertx.executeBlocking(() -> {
Log.info("Vert.x: " + request.get());
            Log.info("Vert.x: " + Vertx.currentContext().getLocal("my-id"));

em.complete("Vert.x");
return null;
});
});
}
It turns out the Vert.x duplicated `Context` is propagated through the `ExecutorService` and `ManagedExecutor`, like Bill suspected, and the CDI contexts are stripped from that. This makes absolutely zero sense to me. Can anyone explain?

LT

so 28. 3. 2026 v 15:26 odesílatel Ladislav Thon <lad...@gmail.com> napsal:

Martin Kouba

unread,
Mar 30, 2026, 3:32:28 AM (4 days ago) Mar 30
to quark...@googlegroups.com, Ladislav Thon, Clement Escoffier
On 3/30/26 09:00, Ladislav Thon wrote:
> I just modified the example code to also use the Vert.x duplicated
> `Context` directly, like so:
>
> @GET
> @Path("/vertx")
> public Uni<String> vertx() {
> request.set("foobar");
> Vertx.currentContext().putLocal("my-id","bazqux");
> return Uni.createFrom().emitter(em -> {
> vertx.executeBlocking(() -> {
> Log.info("Vert.x: " +request.get());
> Log.info("Vert.x: " + Vertx.currentContext().getLocal("my-id"));
> em.complete("Vert.x");
> return null;
> });
> });
> }
>
> It turns out the Vert.x duplicated `Context` /is/ propagated through the
> `ExecutorService` and `ManagedExecutor`, like Bill suspected, and the
> CDI contexts are stripped from that. This makes absolutely zero sense to
> me. Can anyone explain?

If I remember correctly it was introduced here
https://github.com/quarkusio/quarkus/pull/29036 to fix the problem
described in this issue
https://github.com/quarkusio/quarkus/issues/29017#issuecomment-1301992058.
I don't remember the details but maybe Clement (in CC) remembers more.

>
> LT
>
> so 28. 3. 2026 v 15:26 odesílatel Ladislav Thon <lad...@gmail.com
> <mailto:lad...@gmail.com>> napsal:
>
> OK, I think I've got it (couldn't resist). There's a big difference
> between `Vertx.executeBlocking()` and `ExecutorService.submit()` and
> `ManagedExecutor.submit()`, as one can observe using this simple
> program:
>
> @RequestScoped
> public class MyRequest {
> private Stringvalue ="";
> <mailto:quark...@googlegroups.com>> napsal:
>
>
>
> On Sat, Mar 28, 2026 at 6:03 AM Ladislav Thon <lad...@gmail.com
> <mailto:lad...@gmail.com>> wrote:
>
> Dne pá 27. 3. 2026 23:48 uživatel 'William Burke' via
> Quarkus Development mailing list <quarkus-
> d...@googlegroups.com <mailto:quark...@googlegroups.com>>
> napsal:
>
>
>
> On Fri, Mar 27, 2026 at 11:47 AM Ladislav Thon
> <lad...@gmail.com <mailto:lad...@gmail.com>> wrote:
>
> pá 27. 3. 2026 v 15:59 odesílatel Stephane Epardaud
> <stephane...@gmail.com
> <mailto:stephane...@gmail.com>> napsal:
>
> On Fri, 27 Mar 2026 at 15:39, Ladislav Thon
> <lad...@gmail.com <mailto:lad...@gmail.com>>
> wrote:
>
>
> I believe ArC's contexts are propagated
> because the Vert.x duplicated `Context` is
> propagated; the `ArcContextProvider` plays
> no role in it. See https://github.com/
> quarkusio/quarkus/blob/main/extensions/arc/
> deployment/src/main/java/io/quarkus/arc/
> deployment/ArcProcessor.java#L762 <https://
> github.com/quarkusio/quarkus/blob/main/
> extensions/arc/deployment/src/main/java/io/
> quarkus/arc/deployment/
> ArcProcessor.java#L762> -- in fact, it is
> disabled by default.
>
>
> I don't think that's true: https://github.com/
> quarkusio/quarkus/blob/main/extensions/arc/
> deployment/src/main/java/io/quarkus/arc/
> deployment/ArcContextPropagationConfig.java#L19
> <https://github.com/quarkusio/quarkus/blob/main/
> extensions/arc/deployment/src/main/java/io/
> quarkus/arc/deployment/
> ArcContextPropagationConfig.java#L19>
> This was there for Techempower because Sanne
> wanted a boost.
>
>
> OMG it is indeed /enabled/ by default :-D My bad.
> But as Martin says, it should be a noop in most cases.
>
>
> So, I implemented and registered a ThreadContextProvider
> for the session context and my test example now works
> and the session context is propagated.
>
> Can you explain what you mean by "it should be a noop in
> most cases"?
>
>
> OK, you're right and I was wrong. It's been a looong time
> since I last saw `ArcContexrProvider`, so when I was writing
> "it should be a noop", I was thinking it accesses the
> `ThreadLocal` directly. It doesn't. So it does some work,
> but that work should not matter as long as you're always on
> threads that have a Vert.x `Context` associated.
>
> I don't know, maybe the `ManagedExecutor` interferes in that
> it doesn't propagate the Vert.x `Context`? I know that in a
> bare Vert.x application, the `Context` is propagated from
> the event loop thread to the worker thread and back, but I
> didn't try the various executors that exist in Quarkus yet.
>
>
> ManagedExecutor doesn't interfere with anything.  It is the
> vertx extension itself that interferes.  Duplicating the context
> does not copy Arc keys to local data:
>
> [0] https://github.com/quarkusio/quarkus/blob/3.27/extensions/
> vertx/runtime/src/main/java/io/quarkus/vertx/runtime/
> VertxCurrentContextFactory.java#L42 <https://github.com/
> quarkusio/quarkus/blob/3.27/extensions/vertx/runtime/src/main/
> java/io/quarkus/vertx/runtime/VertxCurrentContextFactory.java#L42>
> [1] https://github.com/quarkusio/quarkus/blob/3.27/extensions/
> vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/
> VertxCoreRecorder.java#L603-L607 <https://github.com/quarkusio/
> quarkus/blob/3.27/extensions/vertx/runtime/src/main/java/io/
> quarkus/vertx/core/runtime/VertxCoreRecorder.java#L603-L607>
> [2] https://github.com/quarkusio/quarkus/blob/3.27/extensions/
> vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/
> VertxCoreRecorder.java#L639 <https://github.com/quarkusio/
> quarkus/blob/3.27/extensions/vertx/runtime/src/main/java/io/
> quarkus/vertx/core/runtime/VertxCoreRecorder.java#L639>
>
> I think the reason for this is that MP-CP allows app dev to
> choose what contexts get propagated.  I tested this out by
> adding my custom scope key to the ignore list, and this broke my
> custom scope propagation.
>
> In general, Quarkus currently relies on _two_ APIs to ensure
> contexts are propagated: MP CP and Vert.x duplicated
> `Context`. Some contexts use one, some use the other, ArC
> apparently uses both 🤯 (This is Spartaaaaa!) I'm not sure
> what to think of my attempt to solve the problem by
> introducing a third API...
>
>
>
> Arc *only* uses MC-CP and only for request context.  (See
> above).  I'm actually not sure why why there's a config switch
> [3] to turn off this default behavior. Maybe an artifact of
> backward compatibility?
>
> [3] https://github.com/quarkusio/quarkus/blob/3.27/extensions/
> vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/
> VertxCoreProcessor.java#L293 <https://github.com/quarkusio/
> quarkus/blob/3.27/extensions/vertx/deployment/src/main/java/io/
> quarkus/vertx/core/deployment/VertxCoreProcessor.java#L293>
>
> Thanks for the conversation guys.  I know I answered my own
> questions, but this thread helped me figure it out.
>
> Bill
>
> --
> You received this message because you are subscribed to the
> Google Groups "Quarkus Development mailing list" group.
> To unsubscribe from this group and stop receiving emails from
> it, send an email to quarkus-dev...@googlegroups.com
> <mailto:quarkus-dev...@googlegroups.com>.
> To view this discussion visit https://groups.google.com/d/msgid/
> quarkus-dev/CAL%3DE%3DjRRNqzNsLB3%2Bb7fgWTxE0Lga%3DzLJ-
> NtLwX6SJP5mTOErQ%40mail.gmail.com <https://groups.google.com/d/
> msgid/quarkus-dev/CAL%3DE%3DjRRNqzNsLB3%2Bb7fgWTxE0Lga%3DzLJ-
> NtLwX6SJP5mTOErQ%40mail.gmail.com?
> utm_medium=email&utm_source=footer>.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Quarkus Development mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send
> To view this discussion visit https://groups.google.com/d/msgid/quarkus-
> dev/
> CALbocO%3DYRgw2iX8KrYGBjKL4qFd7sU%3D3L__OpaUAfsjOGK047A%40mail.gmail.com
> <https://groups.google.com/d/msgid/quarkus-dev/
> CALbocO%3DYRgw2iX8KrYGBjKL4qFd7sU%3D3L__OpaUAfsjOGK047A%40mail.gmail.com?utm_medium=email&utm_source=footer>.

Ladislav Thon

unread,
Mar 30, 2026, 4:22:23 AM (4 days ago) Mar 30
to quark...@googlegroups.com
po 30. 3. 2026 v 9:32 odesílatel Martin Kouba <mko...@redhat.com> napsal:
On 3/30/26 09:00, Ladislav Thon wrote:
> It turns out the Vert.x duplicated `Context` /is/ propagated through the
> `ExecutorService` and `ManagedExecutor`, like Bill suspected, and the
> CDI contexts are stripped from that. This makes absolutely zero sense to
> me. Can anyone explain?

If I remember correctly it was introduced here
https://github.com/quarkusio/quarkus/pull/29036 to fix the problem
described in this issue
https://github.com/quarkusio/quarkus/issues/29017#issuecomment-1301992058.
I don't remember the details but maybe Clement (in CC) remembers more.

Ah I see, thanks. The fix is wrong, unfortunately. For whatever reason, we treat executor submission as part of the request, so contexts should be propagated. CDI async observers, on the other hand, escape the request boundary, so no context should be propagated (except tracing).

Since a proper fix is not trivial, I'm adding it to my list of known issues to be addressed. Thanks again!

LT

William Burke

unread,
Mar 30, 2026, 11:02:39 AM (4 days ago) Mar 30
to quark...@googlegroups.com
On Sat, Mar 28, 2026 at 10:27 AM Ladislav Thon <lad...@gmail.com> wrote:
- the `ArcContextProvider` only propagates the request context, not the session context (this honestly feels like a bug)


IMO this is a bug.  I was going to submit a PR to arc for it.
 
--
Bill Burke
IBM

Ladislav Thon

unread,
Mar 30, 2026, 11:58:12 AM (4 days ago) Mar 30
to quark...@googlegroups.com
po 30. 3. 2026 v 17:02 odesílatel 'William Burke' via Quarkus Development mailing list <quark...@googlegroups.com> napsal:
On Sat, Mar 28, 2026 at 10:27 AM Ladislav Thon <lad...@gmail.com> wrote:
- the `ArcContextProvider` only propagates the request context, not the session context (this honestly feels like a bug)


IMO this is a bug.  I was going to submit a PR to arc for it.

Please do. Thanks!

LT 
Reply all
Reply to author
Forward
0 new messages