aeron-io/aeron · error · AeronException
Subscription is closed
Error message
Subscription is closed
What it means
Aeron throws this when addDestination() is called on a Subscription that has already been closed. A Subscription's volatile isClosed flag is set during close(); once set, any conductor-bound mutation such as adding a destination is illegal because the client conductor no longer tracks this registrationId. The error signals a lifecycle bug: the caller is using a stale Subscription reference after teardown.
Solutions
- Check subscription.isClosed() before calling addDestination and skip or recreate the subscription
- Track client lifecycle: call addDestination only while the owning Aeron client is alive (hook ClientConductor/Aeron close callbacks)
- Guard all destination management behind a single owner thread or executor so close and addDestination cannot race
- Catch AeronException and treat the subscription as dead, then re-create subscription + destination
Example fix
// before
subscription.addDestination("aeron:udp?endpoint=localhost:40456");
// after
if (!subscription.isClosed())
{
subscription.addDestination("aeron:udp?endpoint=localhost:40456");
} Defensive patterns
Strategy: try-catch
Validate before calling
if (subscription.isClosed()) { throw new IllegalStateException("subscription closed"); } Type guard
boolean isUsable(Subscription s) { return s != null && !s.isClosed(); } Try / catch
try { subscription.addDestination(endpoint); } catch (AeronException e) { if (e.getMessage().contains("closed")) { recreateSubscriptionAndDestinations(); } else { throw e; } } Prevention
- Always close a Subscription on the same thread/executor that manages its destinations
- Check isClosed() before any destination mutation
- Register an Aeron.ExceptionHandler and unavailableImage/close callbacks to track subscription lifecycle
- Null out cached Subscription references in a close callback so stale uses fail fast with NPE, not after teardown
When it happens
Trigger: Calling subscription.addDestination(endpointChannel) after subscription.close() (or after ClientConductor shut down the subscription, e.g. on driver timeout or conductor.close()).
Common situations: Multi-threaded code where one thread closes the client/subscription while another still mutates destinations; reusing a cached Subscription after reconnecting; failing to null out a Subscription field in a shutdown hook.
Related errors
- unexpected Aeron close
- unexpected driver close
- response publication is closed: " + session
- recording events publication is closed
- client is closed
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/48144c61176acedd.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-client/src/main/java/io/aeron/Subscription.java:474
*
* @return {@link List} of local socket addresses for this subscription.
* @see #channelStatus()
*/
public List<String> localSocketAddresses()
{
return LocalSocketAddressStatus.findAddresses(conductor.countersReader(), channelStatus(), channelStatusId);
}
/**
* Add a destination manually to a multi-destination Subscription.
*
* @param endpointChannel for the destination to add.
*/
public void addDestination(final String endpointChannel)
{
if (isClosed)
{
throw new AeronException("Subscription is closed");
}
conductor.addRcvDestination(registrationId, endpointChannel);
}
/**
* Remove a previously added destination from a multi-destination Subscription.
*
* @param endpointChannel for the destination to remove.
*/
public void removeDestination(final String endpointChannel)
{
if (isClosed)
{
throw new AeronException("Subscription is closed");
}
conductor.removeRcvDestination(registrationId, endpointChannel);View on GitHub (pinned to 6d60124e15)