aeron-io/aeron · error · AeronException

Publication is closed

Error message

Publication is closed

What it means

Publication.addDestination() throws this AeronException when the publication has already been closed. Once closed, its conductor can no longer process destination changes for the (multi-destination) publication.

Solutions

  1. Check publication.isClosed() before calling addDestination.
  2. Reorder lifecycle code so all addDestination calls happen before close(), and remove destinations from shutdown paths.
  3. Guard destination management with the same lock/ownership discipline that closes the publication to eliminate the race.

Example fix

// before
publication.addDestination("aeron:udp?endpoint=localhost:40456");
// after
if (!publication.isClosed()) {
    publication.addDestination("aeron:udp?endpoint=localhost:40456");
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (publication.isClosed()) {
    return; // skip destination management
}

Try / catch

try {
    publication.addDestination(uri);
} catch (AeronException e) {
    if ("Publication is closed".equals(e.getMessage())) {
        // publication torn down; abandon reconfiguration
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling publication.addDestination(endpointChannel) after close() was called, after the publication was closed via the Aeron client, or racing with an async close from another thread / the client conductor closing a released publication.

Common situations: MDC (multi-destination-cast) publications being reconfigured during shutdown; lifecycle bugs where a destination is added in a cleanup path after publication.close(); holding publication references after Aeron.close().

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/785e90287bf3f039. Report an issue: GitHub.

Appendix: source

Thrown at aeron-client/src/main/java/io/aeron/Publication.java:568

     * @param bufferClaim to be populated if the claim succeeds.
     * @return The new stream position, otherwise a negative error value of {@link #NOT_CONNECTED},
     * {@link #BACK_PRESSURED}, {@link #ADMIN_ACTION}, {@link #CLOSED}, or {@link #MAX_POSITION_EXCEEDED}.
     * @throws IllegalArgumentException if the length is greater than {@link #maxPayloadLength()} within an MTU.
     * @see BufferClaim#commit()
     * @see BufferClaim#abort()
     */
    public abstract long tryClaim(int length, BufferClaim bufferClaim);

    /**
     * Add a destination manually to a multi-destination-cast Publication.
     *
     * @param endpointChannel for the destination to add.
     */
    public void addDestination(final String endpointChannel)
    {
        if (isClosed)
        {
            throw new AeronException("Publication is closed");
        }

        conductor.addDestination(originalRegistrationId, endpointChannel);
    }

    /**
     * Remove a previously added destination manually from a multi-destination-cast Publication.
     *
     * @param endpointChannel for the destination to remove.
     */
    public void removeDestination(final String endpointChannel)
    {
        if (isClosed)
        {
            throw new AeronException("Publication is closed");
        }

        conductor.removeDestination(originalRegistrationId, endpointChannel);

View on GitHub (pinned to 6d60124e15)