{"record":{"id":"34d5d15b5ee2ea0c","repo":"aeron-io/aeron","slug":"aeron-client-must-use-a-rethrowingerrorhandler-clusterbackup","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/ClusterBackup.java","lineNumber":789,"sourceCode":"                        .clientLock(NoOpLock.INSTANCE)\n                        .clientName(clientName));\n\n                if (null == errorCounter)\n                {\n                    errorCounter = ClusterCounters.allocateVersioned(\n                        aeron,\n                        buffer,\n                        \"ClusterBackup Errors\",\n                        CLUSTER_BACKUP_ERROR_COUNT_TYPE_ID,\n                        clusterId,\n                        ClusterBackupVersion.VERSION,\n                        ClusterBackupVersion.GIT_SHA);\n                }\n            }\n\n            if (!(aeron.context().subscriberErrorHandler() instanceof RethrowingErrorHandler))\n            {\n                throw new ClusterException(\"Aeron client must use a RethrowingErrorHandler\");\n            }\n\n            if (!aeron.context().useConductorAgentInvoker())\n            {\n                throw new ClusterException(\"Aeron client must use conductor agent invoker\");\n            }\n\n            if (null == errorCounter)\n            {\n                throw new ClusterException(\"error counter must be supplied if aeron client is\");\n            }\n\n            if (null == countedErrorHandler)\n            {\n                countedErrorHandler = new CountedErrorHandler(errorHandler, errorCounter);\n                if (ownsAeronClient)\n                {\n                    aeron.context().errorHandler(countedErrorHandler);","sourceCodeStart":771,"sourceCodeEnd":807,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/ClusterBackup.java#L771-L807","documentation":"ClusterBackup drives its media driver interactions from within an agent and requires the Aeron client's subscriber error handler to be a RethrowingErrorHandler, so client errors propagate to the backup agent instead of being swallowed by a default logging handler. conclude() throws this ClusterException when a custom (non-rethrowing) error handler is configured on the Aeron context.","triggerScenarios":"Concluding a ClusterBackup.Context after supplying an Aeron instance whose context error handler was replaced (aeron.context().subscriberErrorHandler(customHandler)) with anything other than RethrowingErrorHandler.INSTANCE.","commonSituations":"Sharing an application-wide Aeron client configured with a logging error handler; copying driver/client setup code that sets a custom ErrorHandler; embedding ClusterBackup in an app with centralized error swallowing.","solutions":["Use RethrowingErrorHandler.INSTANCE as the subscriber error handler on the Aeron.Context used by the backup.","Create a dedicated Aeron client for the ClusterBackup rather than reusing one configured for the application.","Move custom error logging to a wrapper that still rethrows, or handle errors via the supplied errorCounter/AgentListener instead."],"exampleFix":"// before\nAeron.Context aeronCtx = new Aeron.Context().subscriberErrorHandler(myLoggingHandler);\n// after\nAeron.Context aeronCtx = new Aeron.Context().subscriberErrorHandler(RethrowingErrorHandler.INSTANCE);","handlingStrategy":"validation","validationCode":"if (!(aeronCtx.subscriberErrorHandler() instanceof RethrowingErrorHandler)) { throw new IllegalStateException(\"backup requires RethrowingErrorHandler\"); }","typeGuard":"static boolean usesRethrowingHandler(Aeron.Context c) { return c.subscriberErrorHandler() instanceof RethrowingErrorHandler; }","tryCatchPattern":"try {\n    ctx.conclude();\n} catch (ClusterException e) {\n    if (e.getMessage().contains(\"RethrowingErrorHandler\")) { log.error(\"configure Aeron.Context with RethrowingErrorHandler.INSTANCE\"); }\n    throw e;\n}","preventionTips":["Use a dedicated Aeron.Context for ClusterBackup with RethrowingErrorHandler.INSTANCE","Do not globally replace subscriber error handlers on clients shared with the backup","Surface errors via errorCounter/AgentListener instead of swallowing handlers"],"tags":["configuration","error-handler","aeron-client","incompatible-options"],"backgroundTag":"conflicting-config-options","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"}