aeron-io/aeron · error · ClusterException

counter is closed

Error message

counter is closed

What it means

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.

Solutions

  1. Check controlToggle.isClosed() before calling get()
  2. Ensure you do not read cluster control state after Cluster/Archive container close()
  3. Fix lifecycle ordering so close happens only after all readers have stopped

Example fix

// before
ToggleState state = ClusterControl.ToggleState.get(controlToggle);
// after
if (!controlToggle.isClosed()) {
    ToggleState state = ClusterControl.ToggleState.get(controlToggle);
}
Defensive patterns

Strategy: type-guard

Validate before calling

// guard before reading
boolean readable = controlToggle != null && !controlToggle.isClosed();

Type guard

boolean isOpen(AtomicCounter c) { return c != null && !c.isClosed(); }

Try / catch

try {
    ToggleState s = ClusterControl.ToggleState.get(controlToggle);
} catch (ClusterException e) {
    // counter closed: cluster container has been torn down; stop reading
}

Prevention

When it happens

Trigger: Calling ClusterControl.ToggleState.get(controlToggle) after the AtomicCounter backing the control toggle was closed (e.g. after cluster shutdown or conductor teardown).

Common situations: Client code or tooling reading cluster control state during/after cluster shutdown, race between agent close and a service callback reading the toggle.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/e32e5922c83f0231. Report an issue: GitHub.

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ClusterControl.java:169

         * @param controlToggle to be deactivated.
         */
        public static void deactivate(final AtomicCounter controlToggle)
        {
            controlToggle.set(INACTIVE.code());
        }

        /**
         * Get the {@link ToggleState} for a given control toggle.
         *
         * @param controlToggle to get the current state for.
         * @return the state for the current control toggle.
         * @throws ClusterException if the counter is not one of the valid values.
         */
        public static ToggleState get(final AtomicCounter controlToggle)
        {
            if (controlToggle.isClosed())
            {
                throw new ClusterException("counter is closed");
            }

            final long toggleValue = controlToggle.get();
            if (toggleValue < 0 || toggleValue > (STATES.length - 1))
            {
                throw new ClusterException("invalid toggle value: " + toggleValue);
            }

            return STATES[(int)toggleValue];
        }
    }

    private ClusterControl()
    {
    }

    /**
     * Counter type id for the control toggle.

View on GitHub (pinned to 6d60124e15)