quarkusio/quarkus · error · IllegalArgumentException

Can only work with Quarkus-REST instances: ${sseEventSink}

Error message

Can only work with Quarkus-REST instances: ${sseEventSink}

What it means

SseBroadcasterImpl.register() throws IllegalArgumentException when the supplied SseEventSink is not a Quarkus REST (RESTEasy Reactive) SseEventSinkImpl instance. The broadcaster manages internal queues and requires its own implementation, per the JAX-RS SSE contract which restricts broadcaster/sink mixing across providers.

Source

Thrown at independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseBroadcasterImpl.java:69

        onErrorConsumers.add(onError);
    }

    @Override
    public synchronized void onClose(Consumer<SseEventSink> onClose) {
        Objects.requireNonNull(onClose);
        checkClosed();
        closeConsumers.add(onClose);
    }

    @Override
    public synchronized void register(SseEventSink sseEventSink) {
        Objects.requireNonNull(sseEventSink);
        checkClosed();
        readLock.lock();
        try {
            checkClosed();
            if (!(sseEventSink instanceof SseEventSinkImpl sinkImpl)) {
                throw new IllegalArgumentException("Can only work with Quarkus-REST instances: " + sseEventSink);
            }
            sinkImpl.register(this);
            outputQueue.add(sseEventSink);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public synchronized CompletionStage<?> broadcast(OutboundSseEvent event) {
        Objects.requireNonNull(event);
        checkClosed();

        List<CompletableFuture<?>> cfs = new ArrayList<>(outputQueue.size());
        for (SseEventSink eventSink : outputQueue) {
            CompletionStage<?> cs;
            try {
                CompletionStage<?> sendStage = eventSink.send(event);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Obtain the SseEventSink only via injection of the Sse/SseEventSink provided by Quarkus REST.
  2. In tests, build real SseEventSinkImpl instances or use the Quarkus test facilities instead of mocks.
  3. Remove wrapper/proxy layers around the sink before registering it.
  4. Check classpath for conflicting JAX-RS implementations (e.g. classic RESTEasy) supplying their own SseEventSink.

Example fix

// before
SseEventSink sink = mock(SseEventSink.class);
broadcaster.register(sink); // IllegalArgumentException
// after
@Path("stream")
class StreamResource {
    @Inject Sse sse;
    @GET @Produces(MediaType.SERVER_SENT_EVENTS)
    public void stream(@Context SseEventSink sink) {
        broadcaster.register(sink); // real Quarkus-REST sink
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(sink instanceof SseEventSinkImpl)) {
    throw new IllegalArgumentException("Register only Quarkus-REST SseEventSink instances");
}

Type guard

boolean isQuarkusSink(SseEventSink s) {
    return s instanceof SseEventSinkImpl;
}

Try / catch

try {
    broadcaster.register(sink);
} catch (IllegalArgumentException e) {
    log.warn("Non Quarkus sink rejected: " + e.getMessage());
}

Prevention

When it happens

Trigger: Calling sseBroadcaster.register(sink) with a sink obtained from a different JAX-RS provider, a mock, a custom SseEventSink implementation, or a test double.

Common situations: Unit tests injecting Mockito mocks for SseEventSink; mixing implementations when multiple REST frameworks are on the classpath; wrapping/decorating the sink in a proxy that breaks the instanceof check.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/37474a897786411b. Report an issue: GitHub.