aeron-io/aeron · error · AeronException
Aeron client is closed
Error message
Aeron client is closed
What it means
ClientConductor.ensureActive() is called at the start of every public client operation. When the conductor has been closed (isClosed) it throws AeronException("Aeron client is closed") — after a client is closed all its resources (publications, subscriptions, counters) are released and no further commands are accepted. Calling into Aeron after Aeron.close() (or after the client driver has force-closed the conductor) produces this error.
Solutions
- Coordinate shutdown: stop all producer/consumer threads and join them before calling aeron.close().
- Guard client access with an application-level 'running' flag so no calls happen after close.
- Check aeron.isClosed() (or hold the client behind a lifecycle manager) before submitting new commands.
- Create a fresh Aeron instance if you genuinely need to reconnect after close — clients are not reusable.
- Catch AeronException in background threads and treat it as a shutdown signal rather than an error.
Example fix
// before executor.shutdown(); aeron.close(); // publisher thread still running: publication.offer(buffer); // AeronException: Aeron client is closed // after running.set(false); executor.shutdown(); executor.awaitTermination(5, TimeUnit.SECONDS); // let publishers drain aeron.close();
Defensive patterns
Strategy: try-catch
Validate before calling
if (aeron.isClosed()) {
return; // or recreate the client
} Type guard
boolean usable(Aeron aeron) {
return aeron != null && !aeron.isClosed();
} Try / catch
try {
publication.offer(buffer);
} catch (AeronException e) {
if (e.getMessage().contains("client is closed")) {
// expected during shutdown: stop the worker loop
return;
}
throw e;
} Prevention
- Stop and join all producer/consumer threads before aeron.close()
- Route all client access through a lifecycle manager with a running flag
- Never reuse an Aeron instance after close; create a new one
- Treat this exception in background threads as a shutdown signal
- Avoid closing shared clients in test teardown while async work is in flight
When it happens
Trigger: Using an Aeron instance, or any of its Publications/Subscriptions/Counters, after calling aeron.close(); submitting async commands (addCounter, addPublication) from another thread while shutdown is in progress; agent shutdown hooks or Spring/other lifecycle containers closing Aeron while application threads still publish.
Common situations: Graceful shutdown races: one thread closes Aeron while publisher threads continue sending; reusing a cached Aeron client after an application restart within the same JVM (e.g. hot redeploy); error-handling paths that close the client then attempt cleanup via the same client; tests closing a shared client in @AfterEach while async work continues.
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
- client is closed
- Publication is closed
- counter is closed
- publication is closed
- Aeron client instance must set…
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/26f5fd4c9c7e80eb.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-client/src/main/java/io/aeron/ClientConductor.java:1715
final long registrationId = driverProxy.rejectImage(correlationId, position, reason);
awaitResponse(registrationId);
}
finally
{
clientLock.unlock();
}
}
void onNextAvailableSessionId(final int nextSessionId)
{
lastResponseValue = nextSessionId;
}
private void ensureActive()
{
if (isClosed)
{
throw new AeronException("Aeron client is closed");
}
if (isTerminating)
{
throw new AeronException("Aeron client is terminating");
}
}
private void ensureNotReentrant()
{
if (isInCallback)
{
throw new AeronException("reentrant calls not permitted during callbacks");
}
}
private LogBuffers logBuffers(final long registrationId, final String logFileName, final String channel)
{View on GitHub (pinned to 6d60124e15)