aeron-io/aeron · error · ClusterException
counter is closed
Error message
counter is closed
What it means
NodeControl.ToggleState.get(AtomicCounter) reads the cluster control-toggle counter and first requires it not be closed. A closed counter has no valid value, so reading it would be meaningless; a ClusterException is thrown instead.
Solutions
- Stop polling the control toggle once the cluster/agent is closed (track lifecycle)
- Check controlToggle.isClosed() before calling get and skip the read when closed
- Ensure the AtomicCounter is only closed after all readers have stopped
- If using in tooling, re-create the context/counters instead of reading stale ones
Example fix
// before
ToggleState state = NodeControl.ToggleState.get(controlToggle);
// after
if (controlToggle.isClosed()) { return; }
ToggleState state = NodeControl.ToggleState.get(controlToggle); Defensive patterns
Strategy: type-guard
Validate before calling
if (controlToggle.isClosed()) return ToggleState.NEUTRAL;
Type guard
boolean readable(AtomicCounter c) { return c != null && !c.isClosed(); } Try / catch
try { ts = NodeControl.ToggleState.get(controlToggle); } catch (ClusterException e) { ts = null; /* cluster closed */ } Prevention
- Stop toggle polling before closing the agent
- Check isClosed() on every read
- Close counters only after readers stop
When it happens
Trigger: Calling ToggleState.get with an AtomicCounter that has already been closed — e.g. after the consensus module agent was closed, or while the cluster control toggle is being torn down during shutdown while another thread polls it.
Common situations: Monitoring/management code (e.g. ClusterTermination, snapshots control) polling the control toggle after cluster shutdown; lifecycle races during close.
Related errors
- response publication is closed: " + session
- client is closed
- Publication is closed
- invalid state counter code:
- ConcurrentConcludeException
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/bc8784497d629481.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/NodeControl.java:138
* @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];
}
}
/**
* Counter type id for the control toggle.
*/
public static final int CONTROL_TOGGLE_TYPE_ID = AeronCounters.NODE_CONTROL_TOGGLE_TYPE_ID;
private NodeControl()View on GitHub (pinned to 6d60124e15)