quarkusio/quarkus · error · IllegalStateException
Already registered on a broadcaster
Error message
Already registered on a broadcaster
What it means
SseEventSinkImpl.register(broadcaster) throws IllegalStateException if the sink is already associated with an SseBroadcaster. A sink may belong to at most one broadcaster for its lifetime, per the JAX-RS SSE model.
Source
Thrown at independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseEventSinkImpl.java:80
context.suspend();
response.write(EMPTY_BUFFER, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) {
if (throwable == null) {
context.resume();
} else {
context.resume(throwable);
}
// I don't think we should be firing the exception on the broadcaster here
}
});
}
response.addCloseHandler(this::close);
}
void register(SseBroadcasterImpl broadcaster) {
if (this.broadcaster != null)
throw new IllegalStateException("Already registered on a broadcaster");
this.broadcaster = broadcaster;
}
}
View on GitHub (pinned to e1c734241f)
Solutions
- Register each sink exactly once, on the broadcaster that owns its connection.
- Track registration state in application code and skip if already registered.
- On reconnect, use the newly injected SseEventSink instance rather than reusing the old one.
- Catch IllegalStateException and treat the second registration as a no-op if idempotency is desired.
Example fix
// before broadcasterA.register(sink); broadcasterB.register(sink); // IllegalStateException // after broadcasterA.register(sink); // one broadcaster per sink // broadcast to other clients via broadcasterA too, or create a new sink for broadcasterB
Defensive patterns
Strategy: validation
Validate before calling
if (registeredSinks.contains(sink)) {
return; // already on a broadcaster
} Type guard
boolean isUnregistered(SseEventSinkImpl s) {
return s != null && !s.isClosed();
} Try / catch
try {
broadcaster.register(sink);
} catch (IllegalStateException e) {
log.debug("Sink already registered; skipping");
} Prevention
- Register each sink exactly once on one broadcaster
- Use the fresh sink injected per request on reconnects
- Keep a registry of already-registered sinks
- Avoid registering sinks from lifecycle callbacks that may re-fire
When it happens
Trigger: Calling broadcaster.register(sink) twice — e.g. the same injected SseEventSink registered on two broadcasters, or re-registering after a retry/reconnect handler ran twice.
Common situations: Re-registration on client reconnect without a new sink; registering the sink in both a resource method and a lifecycle listener; CDI scope mistakes causing a shared sink instance across multiple registration paths.
Related errors
- Broadcaster has been closed
- Already closed
- Can only work with Quarkus-REST instances: ${sseEventSink}
- Request already suspended
- retrieving all roles not supported when JAX-RS security cont
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/052dcbecccd33208.
Report an issue: GitHub.