aeron-io/aeron · critical · AgentTerminationException

unexpected Aeron close

Error message

unexpected Aeron close

What it means

ArchiveConductor's doWork duty cycle detects that the owning Aeron client was closed (or an abort was requested) while the archive agent was still running. The library throws AgentTerminationException to shut down the archive agent immediately because it can no longer perform any useful work without a live Aeron client. This is a lifecycle violation, not a transient failure.

Solutions

  1. Ensure shutdown order: stop/close the Archive (archive.close()) before closing the Aeron client it was built with.
  2. Reuse a single shared Aeron client for the Archive and do not close it while the archive agent is running.
  3. In shutdown hooks, stop the agent runner driving doWork() first, then call aeron.close().
  4. If abort was triggered externally, check whether your code calls archive.conductor().abort() or a forced termination path unintentionally.
  5. Check for close() calls from other threads (multiple services sharing one Aeron instance) and remove the premature close.

Example fix

// before
aeron.close();
archive.close();

// after
archive.close();
aeron.close();
Defensive patterns

Strategy: try-catch

Validate before calling

// before relying on archive
if (aeron.isClosed()) {
    throw new IllegalStateException("Aeron client closed; do not start Archive");
}

Try / catch

try {
    archive = Archive.launch(ctx.client(aeron));
} catch (AgentTerminationException e) {
    log.error("Aeron client closed while archive running", e);
    // orderly shutdown: close archive then client
}

Prevention

When it happens

Trigger: Aeron.close() (or the context close) is called on the Aeron client used by the Archive while ArchiveConductor.doWork() is still being driven; or abort() is invoked on the conductor (e.g. during shutdown with a closed conductor).

Common situations: Application shuts down the Aeron client before stopping the Archive service; ordering mistake in a shutdown hook; using Aeron.connect() with an Archive that outlives the client; abrupt process shutdown racing the agent duty cycle.

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

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/ArchiveConductor.java:371

        }
        catch (final InterruptedException ignore)
        {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int doWork()
    {
        final long nowNs = nanoClock.nanoTime();
        int workCount = 0;

        if (isAbort)
        {
            throw new AgentTerminationException("unexpected Aeron close");
        }

        dutyCycleTracker.measureAndUpdate(nowNs);

        final long nowMs = epochClock.time();
        if (cachedEpochClock.time() != nowMs)
        {
            cachedEpochClock.update(nowMs);
            workCount += invokeAeronInvoker();

            if (nowMs >= markFileUpdateDeadlineMs)
            {
                markFileUpdateDeadlineMs = nowMs + MARK_FILE_UPDATE_INTERVAL_MS;
                markFile.updateActivityTimestamp(nowMs);
            }
        }

        workCount += controlSessionAdapter.poll();

View on GitHub (pinned to 6d60124e15)