{"record":{"id":"b973cfb9e3be730f","repo":"aeron-io/aeron","slug":"concurrent-conclude-of-consensusmodule-context-not-permitted","errorCode":null,"errorMessage":"Concurrent conclude of ConsensusModule.Context not permitted","messagePattern":"Concurrent conclude of ConsensusModule\\.Context not permitted","errorType":"exception","errorClass":"ConcurrentConcludeException","httpStatus":null,"severity":"error","filePath":"aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModule.java","lineNumber":1729,"sourceCode":"            try\n            {\n                return (Context)super.clone();\n            }\n            catch (final CloneNotSupportedException ex)\n            {\n                throw new RuntimeException(ex);\n            }\n        }\n\n        /**\n         * Conclude configuration by setting up defaults when specifics are not provided.\n         */\n        @SuppressWarnings(\"MethodLength\")\n        public void conclude()\n        {\n            if ((boolean)IS_CONCLUDED_VH.getAndSet(this, true))\n            {\n                throw new ConcurrentConcludeException();\n            }\n\n            validateLogChannel();\n\n            if (serviceCount < 0 || serviceCount > MAX_SERVICE_COUNT)\n            {\n                throw new ClusterException(\"service count of range [0, \" + MAX_SERVICE_COUNT + \"]: \" + serviceCount);\n            }\n\n            if (null == clusterDir)\n            {\n                clusterDir = new File(clusterDirectoryName);\n            }\n\n            if (null == markFileDir)\n            {\n                final String dir = ClusteredServiceContainer.Configuration.markFileDir();\n                markFileDir = Strings.isEmpty(dir) ? clusterDir : new File(dir);","sourceCodeStart":1711,"sourceCodeEnd":1747,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModule.java#L1711-L1747","documentation":"ConsensusModule.Context.conclude() guards itself with an atomic IS_CONCLUDED flag; if conclude() (or a caller like Context.conclude() invoked indirectly via the media driver/consensus module startup) is re-entered while already concluded, it throws ConcurrentConcludeException. conclude() is not idempotent because it mutates state (creates files, connects Aeron), so a second concurrent call would corrupt configuration. This is an internal-invariant guard for lifecycle misuse.","triggerScenarios":"Calling ctx.conclude() twice, from two threads, or calling code that internally concludes the same Context (e.g. ConsensusModule launching with a context the user already concluded).","commonSituations":"Sharing one ConsensusModule.Context across multiple clustered services or containers; wrapping conclude() in retry logic; double startup after a failed bootstrap where the context object is reused.","solutions":["Call conclude() exactly once per ConsensusModule.Context instance","If a second component needs the configuration, use ctx.clone() and conclude the copy","Do not call conclude() manually when passing the context to ConsensusModule/ClusteredServiceContainer — they conclude it themselves"],"exampleFix":"// before\nConsensusModule.Context ctx = new ConsensusModule.Context();\nctx.conclude();\nnew ConsensusModule(ctx).start(); // concludes again -> ConcurrentConcludeException\n// after\nConsensusModule.Context ctx = new ConsensusModule.Context();\nnew ConsensusModule(ctx).start(); // let the module conclude once","handlingStrategy":"validation","validationCode":"if (ctx == null) throw new IllegalStateException(\"context required\");\n// conclude exactly once; if re-conclusion is possible, keep a boolean:\nif (!concluded) { concluded = true; ctx.conclude(); }","typeGuard":null,"tryCatchPattern":"try { ctx.conclude(); } catch (ConcurrentConcludeException e) { /* context already concluded; reuse it or bail out */ }","preventionTips":["Never call conclude() manually on a context you hand to ConsensusModule/ClusteredServiceContainer","Use Context.clone() for additional concluded copies","Keep one Context instance per cluster node lifecycle"],"tags":["lifecycle","concurrency","configuration"],"backgroundTag":"invalid-state-transition","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"}