{"record":{"id":"052dcbecccd33208","repo":"quarkusio/quarkus","slug":"already-registered-on-a-broadcaster","errorCode":null,"errorMessage":"Already registered on a broadcaster","messagePattern":"Already registered on a broadcaster","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseEventSinkImpl.java","lineNumber":80,"sourceCode":"            context.suspend();\n            response.write(EMPTY_BUFFER, new Consumer<Throwable>() {\n                @Override\n                public void accept(Throwable throwable) {\n                    if (throwable == null) {\n                        context.resume();\n                    } else {\n                        context.resume(throwable);\n                    }\n                    // I don't think we should be firing the exception on the broadcaster here\n                }\n            });\n        }\n        response.addCloseHandler(this::close);\n    }\n\n    void register(SseBroadcasterImpl broadcaster) {\n        if (this.broadcaster != null)\n            throw new IllegalStateException(\"Already registered on a broadcaster\");\n        this.broadcaster = broadcaster;\n    }\n}\n","sourceCodeStart":62,"sourceCodeEnd":84,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseEventSinkImpl.java#L62-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbroadcasterA.register(sink);\nbroadcasterB.register(sink); // IllegalStateException\n// after\nbroadcasterA.register(sink); // one broadcaster per sink\n// broadcast to other clients via broadcasterA too, or create a new sink for broadcasterB","handlingStrategy":"validation","validationCode":"if (registeredSinks.contains(sink)) {\n    return; // already on a broadcaster\n}","typeGuard":"boolean isUnregistered(SseEventSinkImpl s) {\n    return s != null && !s.isClosed();\n}","tryCatchPattern":"try {\n    broadcaster.register(sink);\n} catch (IllegalStateException e) {\n    log.debug(\"Sink already registered; skipping\");\n}","preventionTips":["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"],"tags":["jaxrs","sse","resteasy-reactive","illegal-state"],"backgroundTag":"sse-sink-already-registered","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}