{"record":{"id":"48144c61176acedd","repo":"aeron-io/aeron","slug":"subscription-is-closed","errorCode":null,"errorMessage":"Subscription is closed","messagePattern":"Subscription is closed","errorType":"exception","errorClass":"AeronException","httpStatus":null,"severity":"error","filePath":"aeron-client/src/main/java/io/aeron/Subscription.java","lineNumber":474,"sourceCode":"     *\n     * @return {@link List} of local socket addresses for this subscription.\n     * @see #channelStatus()\n     */\n    public List<String> localSocketAddresses()\n    {\n        return LocalSocketAddressStatus.findAddresses(conductor.countersReader(), channelStatus(), channelStatusId);\n    }\n\n    /**\n     * Add a destination manually to a multi-destination Subscription.\n     *\n     * @param endpointChannel for the destination to add.\n     */\n    public void addDestination(final String endpointChannel)\n    {\n        if (isClosed)\n        {\n            throw new AeronException(\"Subscription is closed\");\n        }\n\n        conductor.addRcvDestination(registrationId, endpointChannel);\n    }\n\n    /**\n     * Remove a previously added destination from a multi-destination Subscription.\n     *\n     * @param endpointChannel for the destination to remove.\n     */\n    public void removeDestination(final String endpointChannel)\n    {\n        if (isClosed)\n        {\n            throw new AeronException(\"Subscription is closed\");\n        }\n\n        conductor.removeRcvDestination(registrationId, endpointChannel);","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-client/src/main/java/io/aeron/Subscription.java#L456-L492","documentation":"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.","triggerScenarios":"Calling subscription.addDestination(endpointChannel) after subscription.close() (or after ClientConductor shut down the subscription, e.g. on driver timeout or conductor.close()).","commonSituations":"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.","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"],"exampleFix":"// before\nsubscription.addDestination(\"aeron:udp?endpoint=localhost:40456\");\n\n// after\nif (!subscription.isClosed())\n{\n    subscription.addDestination(\"aeron:udp?endpoint=localhost:40456\");\n}","handlingStrategy":"try-catch","validationCode":"if (subscription.isClosed()) { throw new IllegalStateException(\"subscription closed\"); }","typeGuard":"boolean isUsable(Subscription s) { return s != null && !s.isClosed(); }","tryCatchPattern":"try { subscription.addDestination(endpoint); } catch (AeronException e) { if (e.getMessage().contains(\"closed\")) { recreateSubscriptionAndDestinations(); } else { throw e; } }","preventionTips":["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"],"tags":["lifecycle","race-condition","messaging"],"backgroundTag":"closed-resource-access","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"}