{"record":{"id":"e32e5922c83f0231","repo":"aeron-io/aeron","slug":"counter-is-closed","errorCode":null,"errorMessage":"counter is closed","messagePattern":"counter is closed","errorType":"exception","errorClass":"ClusterException","httpStatus":null,"severity":"error","filePath":"aeron-cluster/src/main/java/io/aeron/cluster/ClusterControl.java","lineNumber":169,"sourceCode":"         * @param controlToggle to be deactivated.\n         */\n        public static void deactivate(final AtomicCounter controlToggle)\n        {\n            controlToggle.set(INACTIVE.code());\n        }\n\n        /**\n         * Get the {@link ToggleState} for a given control toggle.\n         *\n         * @param controlToggle to get the current state for.\n         * @return the state for the current control toggle.\n         * @throws ClusterException if the counter is not one of the valid values.\n         */\n        public static ToggleState get(final AtomicCounter controlToggle)\n        {\n            if (controlToggle.isClosed())\n            {\n                throw new ClusterException(\"counter is closed\");\n            }\n\n            final long toggleValue = controlToggle.get();\n            if (toggleValue < 0 || toggleValue > (STATES.length - 1))\n            {\n                throw new ClusterException(\"invalid toggle value: \" + toggleValue);\n            }\n\n            return STATES[(int)toggleValue];\n        }\n    }\n\n    private ClusterControl()\n    {\n    }\n\n    /**\n     * Counter type id for the control toggle.","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/ClusterControl.java#L151-L187","documentation":"ClusterControl.ToggleState.get throws ClusterException(\"counter is closed\") when the control toggle AtomicCounter has been closed. A closed counter means the cluster container/control session has been torn down and its state can no longer be read.","triggerScenarios":"Calling ClusterControl.ToggleState.get(controlToggle) after the AtomicCounter backing the control toggle was closed (e.g. after cluster shutdown or conductor teardown).","commonSituations":"Client code or tooling reading cluster control state during/after cluster shutdown, race between agent close and a service callback reading the toggle.","solutions":["Check controlToggle.isClosed() before calling get()","Ensure you do not read cluster control state after Cluster/Archive container close()","Fix lifecycle ordering so close happens only after all readers have stopped"],"exampleFix":"// before\nToggleState state = ClusterControl.ToggleState.get(controlToggle);\n// after\nif (!controlToggle.isClosed()) {\n    ToggleState state = ClusterControl.ToggleState.get(controlToggle);\n}","handlingStrategy":"type-guard","validationCode":"// guard before reading\nboolean readable = controlToggle != null && !controlToggle.isClosed();","typeGuard":"boolean isOpen(AtomicCounter c) { return c != null && !c.isClosed(); }","tryCatchPattern":"try {\n    ToggleState s = ClusterControl.ToggleState.get(controlToggle);\n} catch (ClusterException e) {\n    // counter closed: cluster container has been torn down; stop reading\n}","preventionTips":["Check isClosed() before every counter read in shutdown paths","Coordinate close() ordering between conductor and service threads","Avoid caching counter references past container lifecycle"],"tags":["aeron","lifecycle","closed-resource","atomiccounter"],"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"}