{"record":{"id":"04776cbe9253383e","repo":"aeron-io/aeron","slug":"aeron-client-must-use-a-rethrowingerrorhandler-04776c","errorCode":null,"errorMessage":"Aeron client must use a RethrowingErrorHandler","messagePattern":"Aeron client must use a RethrowingErrorHandler","errorType":"validation","errorClass":"ClusterException","httpStatus":null,"severity":"error","filePath":"aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceContainer.java","lineNumber":949,"sourceCode":"            }\n\n            if (null == aeron)\n            {\n                aeron = Aeron.connect(\n                    new Aeron.Context()\n                        .aeronDirectoryName(aeronDirectoryName)\n                        .errorHandler(errorHandler)\n                        .subscriberErrorHandler(RethrowingErrorHandler.INSTANCE)\n                        .awaitingIdleStrategy(YieldingIdleStrategy.INSTANCE)\n                        .epochClock(epochClock)\n                        .clientName(serviceName));\n\n                ownsAeronClient = true;\n            }\n\n            if (!(aeron.context().subscriberErrorHandler() instanceof RethrowingErrorHandler))\n            {\n                throw new ClusterException(\"Aeron client must use a RethrowingErrorHandler\");\n            }\n\n            final ExpandableArrayBuffer tempBuffer = new ExpandableArrayBuffer();\n            if (null == errorCounter)\n            {\n                errorCounter = ClusterCounters.allocateServiceErrorCounter(aeron, tempBuffer, clusterId, serviceId);\n            }\n\n            if (null == countedErrorHandler)\n            {\n                countedErrorHandler = new CountedErrorHandler(errorHandler, errorCounter);\n                if (ownsAeronClient)\n                {\n                    aeron.context().errorHandler(countedErrorHandler);\n                }\n            }\n\n            if (null == dutyCycleTracker)","sourceCodeStart":931,"sourceCodeEnd":967,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceContainer.java#L931-L967","documentation":"Thrown as ClusterException when the Aeron client used by the ClusteredServiceContainer has a subscriberErrorHandler that is not a RethrowingErrorHandler. The clustered service relies on exceptions from subscriber error handlers propagating up so log failures abort the container instead of being silently swallowed, which would corrupt cluster consistency.","triggerScenarios":"Creating the Aeron client with Aeron.connect(ctx) where ctx.subscriberErrorHandler(...) is set to a custom (non-rethrowing) handler, or a default handler that only logs; building the Aeron client inside the container with a pre-configured context carrying the wrong error handler; letting the container create its own client (ownsAeronClient) after overriding the handler.","commonSituations":"Embedding the container in an application that globally configures Aeron contexts with logging error handlers; copy-pasted driver/client context reuse; replacing RethrowingErrorHandler to suppress image errors without realizing the cluster requires rethrow semantics.","solutions":["Set aeron.context().subscriberErrorHandler(new RethrowingErrorHandler()) on the client context passed to the container","Let the container create its own Aeron client (do not override the error handler) if you don't need custom handling","Wrap custom handling around RethrowingErrorHandler by subclassing and rethrowing after logging","If Aeron.Context defaults were changed, restore the default rethrowing subscriber error handler"],"exampleFix":"// before\nAeron.Context aeronCtx = new Aeron.Context()\n    .subscriberErrorHandler(new LoggingErrorHandler());\naeron = Aeron.connect(aeronCtx);\n// after\nAeron.Context aeronCtx = new Aeron.Context()\n    .subscriberErrorHandler(new RethrowingErrorHandler());\naeron = Aeron.connect(aeronCtx);","handlingStrategy":"validation","validationCode":"if (!(aeronCtx.subscriberErrorHandler() instanceof RethrowingErrorHandler)) {\n    aeronCtx.subscriberErrorHandler(new RethrowingErrorHandler());\n}","typeGuard":"boolean isRethrowing(Aeron.Context ctx) { return ctx.subscriberErrorHandler() instanceof RethrowingErrorHandler; }","tryCatchPattern":null,"preventionTips":["Never replace the subscriber error handler on Aeron contexts used by clustered services","Subclass RethrowingErrorHandler if custom logging is needed, and always rethrow","Audit shared/global Aeron context configuration before embedding the container"],"tags":["aeron","cluster","error-handling","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}